Commit Graph
195 Commits
Author SHA1 Message Date
Jinjing a1667c0297 Open launched agents from mobile AI actions (#35235) 2026-11-12 19:45:35 -08:00
Kelvin Amoaba a3bbd507fd fix(mobile): tell the user when closing a session tab fails (#41404)
The close actions swallowed refusals and transport errors, so a tap on
close did nothing when the host refused (for example while the desktop
window is still loading). Show a short toast instead, with a specific
message for runtime_unavailable.
2026-11-12 06:42:25 +00:00
Neil 176943e8fe refactor(mobile-tasks): group modules by concrete feature owners (#41410)
* refactor(mobile-tasks): group modules by concrete feature owners

* refactor(mobile-tasks): consolidate concrete owner imports

* refactor(mobile-tasks): point recording loaders at concrete owners
2026-11-11 22:40:18 -08:00
Neil 22c7097240 refactor(mobile): organize browser stream, input and pane modules (#41300)
* refactor(mobile): group browser streams, interactions and pane owners

* refactor(mobile): use named browser module type imports
2026-11-11 16:06:30 -08:00
OrcaWinandOrcaWin b1106ba883 test: remove low-value suites, recorder snapshots, and repeated inputs (#41244)
* test: remove weak recorder scenarios and arithmetic suites

* test: remove low-value boundary suites and recorder cases

* test: remove mock suites and trim repetitive boundary inputs

* test: remove shallow provider and browser checks

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-11 14:14:48 -08:00
OrcaWinandOrcaWin ee6b0858cf test: remove automatic reply matrices and weak suites (#41238)
* test: remove automatic reply matrices and low-value suites

* test: remove retired matrix timer exceptions

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-11 13:18:44 -08:00
OrcaWinandOrcaWin 6dd068e672 test: remove weak suites and duplicate recorder replays (#41234)
* test: remove weak suites and duplicate recorder replays

* test: satisfy recorder branch lint gate

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-11-11 12:00:24 -08:00
Kelvin Amoaba 8357cfc193 fix(mobile): show Loading for a new Tasks search instead of the previous search's rows (#41067)
* fix(mobile): show Loading for a new Tasks search instead of the previous search's rows

A new filter or search with no kept rows kept the same provider's rows on screen until the host's read landed, so a slow host looked like search did nothing. Only a re-read of the same list now stands on its rows; desktop already shows its load skeleton here.

* test(mobile): re-record Tasks list goldens for the new-filter spinner

* fix(mobile): hold Tasks paging while a new filter loads

* test(mobile): type the new-search spinner test's GitHub row fully
2026-11-11 08:11:53 +00:00
Neil b5611915f9 refactor(mobile): group usage and shell generation owners (#40783)
* refactor(mobile): group usage and shell generation owners

* refactor(mobile): use direct module types in reset credit adapter
2026-11-10 14:28:58 -08:00
Neil dc9a7abfc1 refactor(mobile): group host catalog credentials and descriptors (#40739)
* refactor(mobile): group host catalog credentials descriptors and connection history

* refactor(mobile): import RPC sender and result types from defining owners

* refactor(mobile): colocate push delivery with registration operations

* refactor(mobile): point push recordings at delivery owner
2026-11-10 02:52:14 -08:00
Neil d78a9b1e9a refactor(mobile): organize native chat by feature ownership (#40619)
* refactor(mobile): group native chat modules by feature owner

* refactor(mobile): consolidate concrete owner imports

* test(mobile): preserve pinned release source addresses

* refactor(mobile): import the recorded launch function type directly
2026-11-09 20:18:03 -08:00
Kelvin AmoabaandEng. Mahmoud Bek ElNemer bd8138b231 fix(mobile): keep the selected GitHub project when switching projects (#39964)
* fix(mobile): keep the selected GitHub project when switching projects

Picking another GitHub Project on the phone snapped back to the first one.
A remembered desktop Board view made the pick throw before it committed,
and a committed pick wrote picker rows (title, summary) into settings,
which the host's strict schema refused, so the next hydration restored
the old project.

Commit only identity fields, ask for a view when the remembered one is
not a table, keep the candidate project's views out of the active list
until it commits, drop late view replies from an earlier pick, and key
Browse rows by project identity.

Co-authored-by: Eng. Mahmoud Bek ElNemer <tt21913@gmail.com>

* fix(mobile): keep github.com implicit in project refs and error on projects with no table view

---------

Co-authored-by: Eng. Mahmoud Bek ElNemer <tt21913@gmail.com>
2026-11-09 22:53:54 +00:00
Kelvin Amoaba f126d6ef37 feat(runtime): tell phones when the desktop refreshes a PR (#40438)
* feat(runtime): tell phones when the desktop refreshes a PR

* fix(runtime): name every checkout a coalesced PR read stood for

* fix(runtime): don't count a read that couldn't load checks as a checks change
2026-11-09 19:16:14 +00:00
Kelvin Amoaba 8b1c394a41 perf(mobile): open Home with last-known workspaces and counts (#40129)
* perf(mobile): open Home with last-known workspaces and counts

* fix(mobile): keep disconnected desktops on Home and judge agents by the client's connect time

* fix(mobile): store the seeded Home counts before removing the old snapshot

* fix(mobile): floor router reads after failures, catch up reads that span a stream reopen, seed the wide host list from Home

* fix(mobile): keep the old Home snapshot until the store can take it, and never re-date stored totals

* perf(mobile): key Home's counts in the host list module so the page boot never loads Home's code

* test(mobile): type the Home catalog adapter's modules with type imports

* perf(mobile): read Home's stored rows in one batch beside the host list, and keep mark-read a delta read

* perf(mobile): paint Home's desktop rows without waiting for the stored counts

* perf(mobile): restore Home's stored rows after the desktop list paints, from its host ids

* perf(mobile): read Home's counts on the connected edge itself, not a render later
2026-11-09 14:06:15 +00:00
Kelvin Amoaba 6f83fb3c46 fix(mobile): floor event-driven reads after failures and catch up reads that span a stream reopen (#40142)
* fix(mobile): floor event-driven reads after failures and catch up reads that span a stream reopen

* test(mobile): pin the event router's read floor after a refused read in a recording
2026-11-09 13:51:51 +00:00
Kelvin Amoaba e22bb7c46c feat(mobile): keep Tasks and new-workspace drafts across kills (#40127)
* feat(mobile): keep Tasks and new-workspace drafts across kills

Task comments (per item and project row), the New issue title and body,
and the New workspace note now live in device-store draft rows. They come
back after the form closes or the app is killed, and only into an empty
field. A comment's setter stays bound to its item, so a send that settles
after the user moved on clears the right row.

* fix(mobile): rebuild comment sends when the draft's item changes

* fix(mobile): key the workspace note by host and keep cleared fields cleared

* fix(mobile): clear only the note a finished create sent

* fix(mobile): count clearing an empty draft field as the user's choice

* perf(mobile): cap field drafts, read sheet drafts on open, retry a failed read once

* fix(mobile): retry a failed draft read after a pause and keep New issue text typed during a create

* fix(mobile): save draft text again once a failed store read can be retried

* fix(mobile): remove a stored draft when an unread field is cleared
2026-11-09 13:51:32 +00:00
Kelvin Amoaba a26277b275 perf(github): use the desktop's PR fallback rule on the phone and forward refreshes (#40128)
* perf(github): use the desktop's PR fallback rule on the phone and forward refreshes

* fix(github): keep a merged fallback PR while the phone has no head
2026-11-09 13:51:12 +00:00
Kelvin Amoaba 57d45efe00 fix(mobile): paint host labels and relay server rows from the phone's store on relaunch (#40086)
* fix(mobile): paint host labels and relay server rows from the phone's store on relaunch

* refactor(mobile): reuse the SSH target summary schema for stored host labels
2026-11-09 03:02:45 +00:00
Kelvin Amoaba fe40fd2576 perf(session-history): answer Workspace and Project from their own sessions (#39971)
* perf(session-history): answer Workspace and Project from their own sessions

Workspace and Project send scopeOnly. The scanner then skips listing and parsing cwd-bucket agents (Claude, Pi, CodeBuddy) outside the scope, keeps the recency pass for the other agents but returns only its in-scope rows, and checks name-matched Claude buckets file by file when their newest transcripts name no in-scope cwd. The tab move lookup and All keep the full scan.

* test(session-history): pin that scope-only drops out-of-scope recency rows

* fix(session-history): keep every row main shows when scope-only skips bucket agents

Parse the whole recency window and keep all of its in-scope rows, bound unverified bucket files apart from confirmed ones and let their parse decide, and keep a scope's rows on screen across a depth change.

* fix(session-history): keep Pi listed for scope-only and let scoped rows stand in across scopes

OMP shares Pi's folder under PI_CODING_AGENT_DIR and Pi's listing decides which files OMP parses, so scope-only keeps listing Pi. Workspace and Project rows now stand in for any scope but All, since the renderer filter narrows them.

* fix(session-history): serve scope-only from a cached full list and NFC-fold bucket names

All and Project send the same paths, so a cached All list answers Project without a rescan. Bucket names and scope prefixes are compared NFC-folded, since CodeBuddy and Pi keep the cwd's own Unicode form.

* fix(session-history): match NFD-named buckets by encoding both Unicode forms

Claude replaces each non-alphanumeric code unit with a dash, so normalizing an encoded name can't recover an NFD cwd. Encode the NFD spelling of each scope path too, and drop the post-encoding NFC fold.

* refactor(session-history): build the scope matcher only for scope-only scans and tighten comments
2026-11-08 22:21:27 +00:00
Kelvin Amoaba 96ac06c00f feat(mobile): add an Agents tab listing every agent across your desktops (#39244)
* feat(agents): send paired phones the desktop Agents list's verdict per agent

While a phone is paired, the desktop window publishes its own Agents-list
classification for each pane (listed, status, unread, clearable, title,
worker parent) and worktree.ps attaches it to each agent row, so the phone
lists agents exactly as the sidebar does. agentActivity.setRead/clear apply
through the sidebar's own Activity actions and resolve once the new
verdicts are stored.

* feat(mobile): add an Agents tab to Home

One row per agent across every paired desktop, grouped by status, with
a desktop filter, search, group-by, nested workers and a long-press
menu. Feed's per-workspace Agents section becomes a short Needs you
list, and the needs-you badge moves to the Agents tab.
2026-11-08 19:31:50 +00:00
Kelvin Amoaba b032aa5615 feat(runtime): tell phones which settings changed (#39718)
* feat(runtime): tell phones which settings changed

* test(mobile): re-record Source Control goldens for the settings stream
2026-11-08 19:12:26 +00:00
Kelvin Amoaba f74bb65fa1 feat(runtime): tell phones when git status changes (#39672)
* feat(runtime): tell phones when git status changes

* fix(runtime): tell phones about HEAD and packed-refs moves too

* fix(runtime): don't let a phone's status re-read join a read begun before the change

* perf(runtime): limit status-read invalidation to the checkouts an outside change touched

* fix(runtime): match a checkout's status keys under any execution host

* fix(runtime): invalidate globally while a primary checkout is unread

* test(mobile): re-record the Changes and settings goldens on the rebased main

* fix(runtime): invalidate globally while a listed checkout has no cached path

* test: follow the moved paired-client harness

* refactor(mobile): let the host list's catalog read hold the event router
2026-11-08 18:36:13 +00:00
Neil 19043a3d76 refactor(mobile): organize pairing, session, RPC and host owners (#39846)
* refactor(mobile): give relay and encryption concrete owners

* refactor(mobile): organize session and review feature owners

* refactor(mobile): organize RPC and host ownership

* test(mobile): point render fixtures at concrete owners

* refactor(mobile): use explicit type-only owner imports
2026-11-08 07:19:05 -08:00
784aefee50 perf(tasks): look up the GitLab comment viewer only on phone reads (#39860)
* perf(tasks): look up the GitLab comment viewer only on phone reads

The phone is the only reader of viewerDidAuthor, so GitLab detail reads
from desktop IPC and the Checks poll skip the `glab api user` lookup.
The phone sends markViewerComments on every GitLab detail read.

Ported from #39749 (GitLab part only); GitHub REST-fallback viewer
lookup is unchanged.

Co-authored-by: AmoabaKelvin <97001695+AmoabaKelvin@users.noreply.github.com>

* perf(tasks): keep GitLab viewer marks by default; desktop detail reads opt out

Older phones and callers that omit markViewerComments keep author marks.

Co-authored-by: AmoabaKelvin <97001695+AmoabaKelvin@users.noreply.github.com>

---------

Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
Co-authored-by: AmoabaKelvin <97001695+AmoabaKelvin@users.noreply.github.com>
2026-11-08 06:27:50 -08:00
Kelvin Amoaba 3e9ca6e85a perf(mobile): open the PR tab with its last state and refresh only what a write changed (#39709)
* perf(mobile): open the PR tab with its last state and refresh only what a write changed

* fix(mobile): allow the phone's comment re-read and settle review findings

* fix(mobile): keep PR reads current across path switches and overlapping writes

* refactor(mobile): share PR query keys and simplify PR write rollbacks

* chore(mobile): list every PR action and comment write kind explicitly

* fix(mobile): read the push-time PR reread through an effect event

* fix(mobile): report a comment write interrupted by a path switch as unconfirmed

* fix(mobile): settle a comment write whose reply was lost from the re-read comments

* style(mobile): format host-query after rebase onto Q12
2026-11-08 06:12:07 +00:00
Kelvin Amoaba b4eda05c81 perf(mobile): show the last task list per filter while it refreshes (#39668)
* perf(mobile): show the last task list per filter while it refreshes

* perf(mobile): read Tasks repos six at a time

* fix(mobile): hold Tasks paging while kept rows re-read

* test(mobile): pin Tasks paging held during a query switch

* perf(mobile): read Tasks repos four at a time, the desktop's gh limit

* test(mobile): give the Linear connect recording the revalidating input

* perf(mobile): open Tasks on its kept hydration and lists through host query families

* fix(mobile): keep the desktop's newer Tasks state and only cancel screen-read queries

* fix(mobile): type the Jira refresh harness and Tasks list read for CI
2026-11-08 05:53:45 +00:00
Kelvin Amoaba db7ff5c8ff fix(mobile): re-subscribe Home and the follow-up preference after the client event stream ends (#39655)
* fix(mobile): re-subscribe Home and the follow-up preference after the client event stream ends

* fix(mobile): try a refused client event wire once per connected edge

* refactor(mobile): mark replayed client event readies in the shared stream

* test(e2e): give the phone feed socket client a state listener
2026-11-08 05:52:38 +00:00
Kelvin Amoaba 34cd1161bb perf(mobile): show the workspace list from memory and refresh it on desktop changes (#39453)
* perf(mobile): show the workspace list from memory and refresh it on desktop changes

* fix(mobile): import the host list queries type without an import() annotation

* fix(mobile): keep host labels on a not-connected read and update the phone feed e2e helper

* fix(mobile): keep the event router listening when its stream ends while opening

* fix(mobile): reuse the shared read-view base and remount for the cache-return recording
2026-11-08 00:23:19 +00:00
Kelvin Amoaba 11f31112db perf(files): page large folders on the phone with Show 500 more and Search all (#39438)
* perf(files): page large folders on the phone instead of reading them whole

* fix(files): page from hasMore, refresh closed folders, retry a superseded Show more

* fix(files): rerun a refresh that paging displaced, and keep an old host's whole folder from duplicating rows

* test(mobile): re-record the Files matrix goldens for paged reads

* test(files): type the paging fake host's params without widening

* fix(files): filter folder reads that land after the folder closes
2026-11-07 23:43:43 +00:00
Kelvin Amoaba a9f65f71d2 feat(mobile): handle renames and the workspace list while the desktop is offline (#39448)
* feat(mobile): add host mutations with draft, block and queue modes, starting with rename

* fix(mobile): settle a lost rename through the reconnected client and keep server rows routed

* fix(mobile): offer Retry on the offline banner once the host is unreachable

* fix(mobile): let the offline banner's Retry redial from the page

* fix(mobile): date the offline banner from the drop, not the connect

* fix(mobile): keep the terminal rename sheet's state updates pure

* test(mobile): mount rename and close-action tests inside a query client

* refactor(mobile): share one draft rename sheet between workspace and terminal rename, and retry a confirm after a path switch

* fix(mobile): keep another target's unsaved rename and show the offline reason over an old refusal

* fix(mobile): keep a newer rename draft when an older save lands late
2026-11-07 23:43:33 +00:00
Kelvin Amoaba ed3a3fd9f6 perf(mobile): reuse the connection's status in the automation editor and chat capability check (#39345)
* perf(mobile): route the last status.get callers through the shared status read

* refactor(mobile): drop the unused one-shot Codex reset capability read

* fix(mobile): open the chat transcript when the text-append check fails, and key connections on one epoch
2026-11-07 23:27:28 +00:00
Kelvin Amoaba de6c2b6f5c perf(mobile): open a session with one terminal list read and a loading body under the cached tabs (#39412)
* perf(mobile): open a session without the startup terminal polls

* test(mobile): re-record session startup goldens

* perf(mobile): cache the session strip from host snapshots and drop phone-closed tabs

* fix(mobile): keep a shown terminal over the offline and loading body states

* test(mobile): re-record the last-strip reopen golden

* refactor(mobile): drop a startup guard that can no longer fire

* fix(mobile): drop a pending-terminal activation reply from an earlier route

* test(mobile): type the pending terminal tab fixture

* test(mobile): import the last-tabs module type without an import() annotation
2026-11-07 23:20:40 +00:00
Kelvin Amoaba 481f61152d perf(mobile): reopen agent session history from memory (#39387)
* perf(mobile): reopen agent history from memory

* fix(mobile): reread history after association changes without restarting reads, and share the connection's status with query reads

* fix(mobile): reread history after an association change only when the query can read, including after a failed scan

* refactor(mobile): name every history screen state

* fix(mobile): keep history's connected-edge watcher by not gating its query on the connection
2026-11-07 23:14:37 +00:00
Kelvin Amoaba 2ec6ea4aad perf(mobile): prefetch the next and previous file in Review (#39355)
* perf(mobile): prefetch the next and previous file in Review

* perf(mobile): open Review on the tapped file without reading the first file

* fix(mobile): show failed Review diff reads and re-read refused deleted files

A re-keyed worktree diff that fails now shows its error instead of the
older lines. A deleted-file stand-in from a refused read is re-read on the
next visit and by Retry. Without git status, the branch section no longer
waits on an unused compare.
2026-11-07 23:14:26 +00:00
Kelvin Amoaba b0cd6d391c perf(mobile): open a session with its last tab strip (#39158) 2026-11-07 04:55:01 +00:00
Kelvin Amoaba 1763014bea perf(mobile): reopen Files folders from memory (#39175)
* perf(mobile): reopen Files folders from memory

* test(mobile): re-record Files explorer goldens for query reads

* fix(mobile): paint cached Files rows before the host client attaches

* refactor(mobile): share query observer options and Files' empty-state rules

* test(mobile): type the Files explorer test host
2026-11-07 04:41:21 +00:00
Kelvin Amoaba 51f532115e perf(mobile): open Review with the Changes data already loaded (#39159)
* perf(mobile): open Review with the Changes data already loaded

* fix(mobile): keep the open diff through re-keys and wait for Review rebuilds

* fix(mobile): release Review writes once their files show, retry only fixed diffs

* fix(mobile): follow language switches in Review's diff states

* fix(mobile): keep Changes' compare key out of render and type the Review adapters
2026-11-07 04:41:07 +00:00
Kelvin Amoaba 22f09f4257 refactor(mobile): read host capabilities from one status read per connection (#39157) 2026-11-07 04:04:19 +00:00
Kelvin Amoaba 9dacf1e6db fix(mobile): make dictation start reliably on a slow or sleeping desktop (#39142)
* fix(mobile): keep speech said while dictation starts and fail fast on a dead host

* fix(mobile): let a tap or long-press end dictation while it starts

* fix(mobile): wait for the host's cancel before the next dictation starts

* fix(mobile): let a start wait out the previous start on the host too

* refactor(mobile): track host dictation work per client, not per screen

* test(mobile): read the start's dictation id without a cast

* fix(mobile): finish instead of cancel when a held mic is released while starting
2026-11-07 01:25:34 +00:00
Neil bb5f37c916 feat(pairing): unscanned desktop pairing codes expire (QR 10 min, link 60 min) (#37957)
* feat(pairing): unscanned desktop pairing offers expire (QR 10 min, link 60 min)

Headless serve/orcad mint offers without a TTL, so they never expire.
Paired rows and rows without offerExpiresAt are unaffected.

* feat(pairing): refresh the mobile QR before it expires and show runtime link expiry

The QR re-mints a minute before expiry while visible; an unused runtime
link shows its expiry and, once expired, is replaced by a notice. Web
clients rejected while pairing now suggest asking for a new link.

* feat(mobile): say a refused pairing code expired or was replaced

A first pairing the desktop refuses (direct or Relay) now asks for a new
code instead of showing the raw transport error.

* refactor(pairing): replace type assertions in code-refusal detection and tests

* test(mobile): re-record pairing race golden for refused-code error

* fix(pairing): keep pairing codes valid across sleep and stale registry writers

- QR refresh rechecks the wall clock every 15 s and on window focus, so a
  computer waking from sleep never keeps showing an expired code.
- A registry save from a process with an older copy can no longer reset a
  paired row back to unscanned (which would let its offer expire).
- The QR caption says how long a copied code works.

* test(pairing): annotate the IPC handler stub cast
2026-11-06 16:23:44 -08:00
Kelvin Amoaba 489a6a66a4 perf(mobile): reopen Source Control's Changes list from memory instead of rescanning (#38988)
* perf(mobile): reopen Source Control's Changes list from memory instead of rescanning

* fix(mobile): keep action errors through background status reads

* fix(mobile): let only explicit status reads clear Source Control's action error

* refactor(mobile): simplify the query layer and the Changes compare trigger

* fix(mobile): follow language switches in Changes' empty states
2026-11-06 16:46:31 +00:00
Kelvin Amoaba f6653a44d5 fix(mobile): keep the rename sheet open until the desktop accepts (#38934)
Terminal rename closed its sheet before sending terminal.rename, then
ignored a refused reply and swallowed errors, so the typed name vanished
with no message. The sheet now stays open with the name while the desktop
answers, shows the refusal with Retry, and closes only after the desktop
accepts, the same way the workspace rename sheet works.
2026-11-06 16:45:44 +00:00
Kelvin Amoaba f9efd9fbaa fix(mobile): keep Tasks drafts across reconnects and item updates (#38935)
Reconnects no longer close the open item, project row, create sheet or
workspace form, and draft resets key on item identity instead of the
item object, so status edits and refreshes keep typed comments. The
"+" button no longer clears the create title and body.
2026-11-06 16:43:30 +00:00
Kelvin Amoaba c884428609 feat(mobile): copy, save and share an image from the full-screen image viewer (#38303)
* feat(mobile): copy, save and share an image from the chat image viewer

* refactor(mobile): pass image viewer actions in so diagrams skip them

* fix(mobile): name the image verb native.image.act and serialize image actions

* fix(mobile): queue image actions per page and skip SVG links

* refactor(mobile): share the cache-folder helper and table the viewer's image actions

* fix(mobile): keep the toast's live region mounted so screen readers hear it

* test(mobile): re-record agent history goldens for the always-mounted toast region

* test(mobile): import the declared routes in the moved routes test
2026-11-05 22:44:36 +00:00
Brennan Benson dd1784f55b Keep partial check details from replacing complete PR status (#38514)
* Keep PR check status tied to complete observations

* Correct checks presentation types and count import

* Preserve localized empty states and declared runtime support

* Correct discovery fixture stack types

* Omit empty failed-check subtitles

* Preserve check identity through refresh and row publication

* Withhold row identity for malformed check errors

* Limit local capability wakes to local task sources

* Correct PR check regression fixtures

* Preserve browser APIs in PR check regression fixture

* Settle PR check adoption after failed detail reads

* Use the parsed PR check detail type during refresh

* Preserve PR metadata across checks discovery and committed query lifetimes

* Keep complete checks proof separate from legacy publication policy

* Keep PR lookup fixtures independent from complete checks collection

* Keep complete-check fixtures within lint contracts

* test: align PR recordings with source check discovery
2026-11-05 10:39:54 -08:00
Kelvin Amoaba b8969c9c53 feat(mobile): split Home into Feed, Usage, Automations and Settings tabs (#38206)
* revert(mobile): drop the details card on session tab taps

Reverts 852391da0f (#36395). The card popped up on every tab switch.
Tapping a tab switches tabs again, and the session title card goes back
to its own owner from #30389. Keeps later changes to these files: tab
press feedback, the compact top bar's + button, and #36866's keyboard
keep-focus for title taps.

* fix(mobile): hide branch and folder when the setting is off

The phone's workspace row always printed the branch or folder path, so the
Branch name / Branch / folder path toggle under Card display did nothing.
The shared card details now carry that rule, and the row reads it.

* fix(mobile): centre the chevron on Home list rows

* fix(mobile): scroll sheets taller than the screen

Content-sized sheets ride the keyboard by translation, but their height cap ignored that lift, so a tall sheet pushed its top off screen with no way to scroll back to it. The cap now gives back the keyboard lift. Sheets that scroll their own list can now shrink to the cap instead of clipping it, and the GitHub Pages picker no longer nests its list inside the drawer's ScrollView.

* feat(mobile): show each desktop's machine kind and model

The desktop now reports whether it is a laptop, desktop or server, and its
model, on status.get. The phone stores it with the other last-known host
fields and turns it into a line like "MacBook Pro · macOS".

* feat(mobile): edit a host from its screen and align the Automations bar

The host screen's menu gains Edit host and Network diagnostics. Edit host moves
onto the standard bar and gains Remove host. Automations uses the standard bar
with its switcher pinned under it, as Source Control does.

* feat(mobile): arrange Home and bring back stats and account usage

Home drops the Jump back in times, names the repo on every Jump back in row
(#37642), shows each desktop's machine kind, and adds stats and account usage
at the bottom. A new Arrange button reorders and hides sections, saved on the
phone.

* fix(mobile): keep the page bundle within budget and show the machine on the host screen

The edit route gets a web stub so its native-only removal flow stays out of
the page's chunks, and Automations keeps its own segment style. The host
screen header shows the machine line like Home does.

* fix(mobile): give the edit route's page stub its own module

A bare re-export of the catch-all route compiled to the same asset as
[...page], so the page manifest listed it twice and the desktop template's
inventory check refused the build.

* feat(mobile): turn Home into Feed, Usage, Automations and Settings tabs

Feed keeps agents (needs-you first, five then Show all), desktops with a
needs-you count, and Tasks; New workspace moves to a floating button.
Usage shows stats and per-desktop account usage, Automations a per-desktop
summary with on/off switches that opens the page for editing (new=1 opens
its editor). Settings becomes a tab. Arrange Home is removed: tabs replace
the long scrolling Home it was for.

* style(mobile): darken the accent blue slightly

* style(mobile): give the Home Tasks row the desktop tile icon

* style(mobile): condense the Home Feed

Smaller tiles, type and gaps; a desktop is two lines with its needs-you
count as an amber pill; the Tasks row drops its duplicate heading.

* style(mobile): drop the Last known prefix on Home desktop rows

* chore(mobile): bump to 0.0.63 and Android versionCode 27

* test(mobile): re-record Linear task recordings for the darker accent blue

* test(mobile): check the page Back stack on Tasks now that Edit host is native

* fix(mobile): pass the Usage tab's limit window to the shared usage helpers

* fix(mobile): type the Usage tab test to the account-slot limits and keep the tab under 400 lines
2026-11-04 20:35:53 +00:00
Kelvin Amoaba 8a4dd3a7bb feat(mobile): improve mobile terminal input (#37900)
* feat(mobile): bring back the terminal key row and one live input

Every terminal tab, shell or agent, gets the dock from before #36166:
the scrolling key row (desktop width, Paste, the built-in keys, custom
keys, add) above one live field that types straight into the terminal,
with the attach and mic buttons beside it. The field keeps today's 3:1
outline and focus ring, and the keys use today's chips and press styles.

The Keys/Message toggle, the Message composer, the arrow pad, the ⋯
sheet on the row and the per-terminal mode preference go. A stored mode
from an older build is never read. Sticky ctrl/alt/shift and the More
keys sheet stay only behind the iPhone Duo rail, which shipped shells
still draw; the page reports no composer, so shipped shells grey their
input-mode key and newer shells hide it.

* refactor(mobile): delete the buffered terminal line

With one live input there is no way to write a terminal line first and
send it, so the per-terminal drafts and their storage, the Send path,
command-field dictation, draft restoration and the terminal setting for
autocomplete and autocorrect (which only applied to that line) go.
Dictation in a terminal always types into it live. Drafts and the setting
saved by older builds are never read.
2026-11-04 06:01:18 +00:00
Brennan Benson a9676ea6eb Distinguish cancelled PR checks from failures (#37789)
Show cancelled PR checks as Cancelled with separate counts on desktop and mobile, preserving genuine failures, pending checks, existing action permissions and older-client fallback. Carry complete provider summaries through caches and presentation; preserve Azure source completeness, folder attention priority and muted cancellation details.

Tracked in #15847 and STA-5102.
2026-11-03 18:23:26 -08:00
Brennan Benson a56e6de883 Keep phone fork pull requests on their selected repository (#36762)
* Keep opened GitHub work items bound to their exact repository

* Bind GitHub work item ownership through consumers and dispatch

* Keep empty mention lists from consuming selection keys

* Preserve GitHub action and consumer ownership across resource changes

* Exercise repository account updates through their production store action

* Exercise account publication and cache settlement through GitHub consumers

* Simplify fork PR contract verification

* Make contract verification imports and clocks deterministic

* Correct mobile task test fixtures and model types

* Correct task recording fixtures and CI types

* Correct task fixture types and static checks

* Consolidate replay test RPC imports

* Separate GitHub metadata imports from Linear hooks

* Scope mobile PR sidebar reads to the selected resource

* Separate desktop GitHub changes from phone updates

* Preserve complete phone GitHub repository identity updates

* Fix relay authority probe census across topology cases

* Repair repository owner proof fixtures and compiler allocation

* Preserve repository row observation in host lookup

* Seed metadata fixture before server-side store acquisition

* Preserve nullable fixture settings and discover mixed test types

* Refetch phone reviewer list after a connection change

A disconnect or session change while the reviewer list loaded left the drawer showing an empty list until it was reopened. The drawer now shows its loading state while disconnected and fetches the same repository again once connected, still ignoring the cancelled load's result. Also merges a duplicate type import that failed mobile lint.

* Keep phone reviewer rows through connection changes

The reviewer picker now owns one query per open visit of a client, worktree and repository. A connection or session change retires the in-flight request at once but keeps the people already loaded; reconnecting refreshes them in the background. Without loaded people the drawer shows its spinner, or the existing connection explanation once an outage is revealed, and never a false empty list. Closing, retargeting or reopening starts a fresh visit that no earlier reply can paint. Also wraps the synchronous bridge teardown steps so the cleanup loop awaits only promises.

* Keep the viewed phone PR through connection changes

A relay/direct switch or reconnect selected a fresh, hidden PR observation in Source Control and Diff Review, so the next render unmounted the PR, closed the reviewer picker and erased an unsent comment. The PR observation is now scoped to the client, worktree and branch. One connection listener retires the in-flight reads when the connection changes and repeats the query the screen already asked for once connected, so accepted content stays mounted and refreshes in place. A failed background refresh keeps the accepted result. Task PR files likewise keep their loaded contents and expansion across a connection loss and read an interrupted file again on reconnect.

* Show a failed PR refresh over kept content; recover task files on every connection change

A failed refresh of an accepted PR (or no-PR) result now keeps the content and shows the existing "Failed to refresh pull request." line with Retry above it, whoever started the read, until the next good read. Task PR file reads are retired and re-read from the task selection's own connection listener, so a reconnect batched into one render or a connection switch at the same state also recovers an interrupted file. The reviewer-picker contract test leaves the React Native test program for now; its checked owner follows.

* Typecheck the phone/host contract tests through emitted declarations

The reviewer-picker contract test and the recorded request params test import real desktop sources, which do not typecheck under the phone's React Native libraries. Both source graphs now emit declarations under their own compiler settings, and a strict Node program checks the two test bodies against them; Vitest still runs them against the original sources through path aliases. The tests typecheck ratchet runs this program and fails unless both bodies are in it and it passes, the CI test runner checks that Bun ran and passed both, and the Mobile workflow finishes compiler work before starting tests. The six other excluded tests stay runtime-only and are labelled that way.

* Name the types phone declaration emission needs

Declaration emission for the phone/host contract program could not name the i18n instance's type or the buffered terminal draft send type; annotate the one and export the other. Also types the drawer mock's children in the Source Control scenario so the React Native test program checks it.

* Stub every icon in the Source Control scenario

Source Control modules read icons at import time, so the scenario's fixed icon list failed to load the file under Bun.

* Give the recorded params check's stand-in method a permission

The recorded request params test is now typechecked and its stand-in method lacked the permission every RPC method declares; parsing never reads it.

* Use the real composer styles in the Source Control scenario

Source Control's list styles read composer style constants at import time, so stubbing that module failed the file under Bun.

* Mock the PR sidebar hooks without loading their native modules

Only the PR sidebar reads these hooks, so the Source Control scenario's mocks need not load the real modules, whose imports reach Expo native code that does not run under Vitest.

* Count recording-internal observations apart from recorded ordinals

The scripted transport runs the real request tracker, and its connection wait, id allocation, completion, late-reply and terminal observations each took the shared write ordinal that requests, frames and effects record. Every recording's ordinals shifted although the same frames went out in the same order. Those observations now use their own counter, keeping their association with the native request, so recorded ordinals match the existing recordings again.

* Record a cleanup checkpoint only when teardown adds effects

Comparing the whole snapshot before and after teardown added a cleanup checkpoint whenever a pending request settled at dispose, which changed about 166 recordings without new evidence. Restore the condition the recordings were made with: teardown effects alone add the checkpoint.

* Run the bridged recording corpus on Node

Its goldens hold V8's TypeError wording, which the Bun pass reports differently, so ten exact error comparisons failed only on engine text. It now runs with family-recordings on Node; the comparison stays exact.

* Bind the probe test's fresh status.get by its actual child

* Edit the GitLab status scenario's GitHub issue only after opening it

* Type the Source Control scenario's composer adapters with the real component props

* Mock the Source Control scenario's icons and protocol gates without loading the real modules

* Give the PR sidebar probe test the shared typed fake client

* Select the GitLab status scenario's GitHub issue as a typed task item

* Declare the buffered terminal draft send as an exported type

* Run the Source Control scenario's focus effect only when its callback changes, as the router does

The router mock ran the loaders' focus effect after every commit, so each status read re-rendered and re-ran it. Also mock AppState for the comments section and stop act from awaiting held PR reloads.

* Re-record the GitHub and Tasks goldens for the selected-repository requests

Re-recorded with pnpm --dir mobile rpc:record for the 71 scenario IDs: 66 changed and 5 added for the exact-target status.get probe, 0 removed. The goldens now carry the selected repository on GitHub requests, the exact-target probe before GitHub operations, the skipped hidden metadata loads and the new item-detail failure copy.

* Declare the probed GitHub operations' params in the order the app sends them

The bridge probe test compares the forwarded frame byte for byte with the declared params, so tk-item-detail-github, tk-item-status-gitlab and tk-project-row-files-merge now declare the exact key order their goldens record. Values are unchanged, and completion matching ignores key order.

* Close the native connection before the page in recordings, and make late replies and inherited timers explicit

Recording teardown now closes the native client, flushes its rejection through the bridge, then closes the page, so page callers see the relayed native close. Sibling rejections name the stable logical site in every mode. Replies scripted after a timeout or disconnect are explicit late replies that must resolve nothing. Four product timers inherited from main are declared by producer, scenario, owner and delay and still reported; every other run keeps zero residual timers. The bridge probe oracle for tk-item-status-gitlab expects the published deadline failure and the exact retained issue.

* Judge unchanged bridged recordings as main does and changed ones by their own outcomes

Unchanged producers follow main's order again: identical earns identical, History keeps its strict path, and other divergences take a legacy label from the reply-meta counterfactual's typed evidence. A bounded guard pins zero unclassified, small-class membership and the branch-base tally less each named moved or removed producer; eight moved producers without a base pin are named as old-class-pending until their baseline replay. The re-recorded producers are judged directly in both page modes against their reviewed goldens, with only the page's refusal of the injected absent result applied, or by their exact strict own-undefined stop, and are pinned as bridge contracts. Bridge-only preludes are observed after their action, and the pending invocation's relayed native close is read from its own page promise. Every producer must be routed exactly once.

* Say what to do when the GitHub server check fails, and share one deadline with the action

The exact-target check runs before a GitHub action is sent, so a timeout or lost connection now shows the existing 'The Orca server connection changed. Try again.' and a refused or unreadable answer shows the existing 'Update Orca on the executing server' message, instead of raw text naming status.get. The check's own connection-changed error passes through. On native, the checks and the action share one 30-second budget, so a dead server fails in 30 seconds instead of 60.

* Pass the scenario manifest to the changed-producer comparison

* Correct the bridged corpus repairs from their first full run

The re-recorded producers no longer stop on the strict own-undefined matcher and all replay in bridge mode, so they are all judged directly; the page's refusal of the GitHub server check now expects the connection-changed copy. Unchanged legacy labels read their first difference from the replay without injected metadata. The sibling timeout and each interruption, including the cutover, end the requests the goldens record as ended, so those replies are explicit late replies. New producers in pinned families get their bridge-contract pins, and an unexpected timer or changed outcome now names its scenario and values.

* Restore six unchanged scenarios to their single own-undefined steps

tw-smart-search-all-providers, tw-smart-search-linear-listed, tk-list-gitlab-items, review-mark-reviewed-persists, review-mark-reviewed-rolls-back and session-create-terminal-without-active-tab had their own-undefined steps split into native and bridge copies, so the page replay no longer reached the strict own-undefined stop and these unchanged producers left their baseline labels. Their steps are the branch-base steps again; native recordings are unchanged.

* Fill the eight moved producers' base classes from the admitted baseline replay

pr-read-surface was identical and the seven github.pr-read matrices were result-absent-settlement at the branch base, so the unchanged tally is exact again with no pending members.

* Deliver the sibling disconnect's trailing replies as late replies too

A disconnect rejects preflight.check, linear.status and jira.status before the suffix replays their replies, exactly as the sibling timeout does, so those declared replies are late in both variants; the cutover stays strict.

* Expect the page's refusal in the two Linear producers' result-absent checkpoints

Natively the injected { ok: true } reads as an empty result, so the Linear detail and status update finish; the page refuses that frame and the screen shows the refusal instead. Each producer now has one finite expectation for that checkpoint, guarded on the reviewed golden's final effects, and everything else must still equal the golden.

* Share one deadline between the shell's server check and the GitHub action

The shell gave the exact-target check and the forwarded action each the page's full timeout unless the page asked otherwise, so a dead server took twice as long to fail on the hosted page as on native. The two legs now always share the page's one deadline; the forwarded options are unchanged.

* Deliver the sibling client cutover's trailing replies as late replies too

* Build the changed producers' page expectations from one finite table before comparing

* Re-record the three exact-target check goldens for the mapped copy

The GitHub check now shows the update-server or connection-changed copy instead of raw reply text; the item detail's empty-message cases now also record their detailError effect. Recorded on the admitted run (check-3949 Job 1).

* Declare the traced inherited timers per inner scenario and recording mode

Each declaration now names the modes where its inner scenario leaves the timer. Adds the inner scenarios traced in check-3949/qa-report-trace.md Job 2, with the ruled delays and owner chains: the reveal's 300 ms first-poll timer for all ten reply partitions of tabs.list-1 and tabs.activate-1 in both modes, and the pr-triage 20,000 ms reconnect waiter for transport-rejection and transport-rejection-no-message in both modes and result-absent through the page only. Exact counts, owners and delays stay enforced.

* Expect the compiler wait before Mobile verify's tests and only the fastlane tools after

* Call recorded timer callbacks through a typed signature

* Show the reviewer picker's no-rows wait with the screen header's connection verdict

The drawer classified only its client's state and counters, so during relay recovery it flipped between Disconnected and Connecting on every dial and never showed relay or pairing verdicts. The source control and diff review screens now pass the host's own verdict inputs (reconnect attempts, last connect, relay recovery status), re-read on host state and path changes, so the drawer shows the same steady verdict as the header.

* Say a timed-out GitHub target check timed out, on native and in the shell

A server check that ran out its own deadline on a connection that never changed now reads "Timed out confirming the executing Orca server.", as the shell's budget already did, and the native shared budget running out uses the same copy. A lost or changed connection still reads "The Orca server connection changed. Try again."

* Name the paired desktop when it is the build missing the exact-target check

When the phone's paired desktop relays to an updated executing server but lacks the exact-target capability itself, the edit now says "Update Orca desktop" instead of pointing at the executing server. An old executing server still gets the executing-server copy.

* Preserve GitHub detail argument types and optional capabilities

* Say what updating the paired desktop enables, as the executing-server copy does

* Drop the tab-reveal timer declarations now that main removed the reveal operation

* Let keyed details queries own pending read replacement

* Repair GitHub RPC and relay test fixture boundaries

* Own the new PR tab's caches, sheets, confirmations and polls by the complete PR

Fork #N and upstream #N were one owner to the tab: check details, open sheets and confirmations keyed by worktree and number survived a repository change, so a fork draft or decision could act on upstream and fork check data could show for upstream. Each is now owned by the PR incarnation (client, worktree, branch and full resource key), selected during render, so a departure closes old requests without retargeting them, A -> B -> A starts fresh, an outgoing sheet renders nothing and sends nothing, a late save closes only its own request, and a departed PR's poll neither blocks nor re-arms its successor.

* Load the reviewers sheet's people through the selected repository's resource-owned query

The new sheet read people once on the first search with no repository, connection recovery or retry, so a fork PR listed the wrong repository and a search while reconnecting stuck on an error. The first search of an open sheet now demands the selected repository's list through the exact-target helper; the query reads only while connected, retires its attempt on every real state or generation change, recovers on reconnect, keeps accepted rows and search through refreshes, and shows the header's own calm connection verdict while it has no candidates. Closing or a PR departure ends the visit; reopening starts fresh.

* Mark a failed PR discovery from every caller, polls included, and keep a poll's checks-only failure quiet

Main's poller made a failed background discovery silent, narrowing the ruled all-caller contract. Any current failed discovery over an accepted PR or no-PR result now keeps it and shows the one refresh-failed row with Retry, and the poll still backs off on 'failed'. A poll whose discovery succeeded but whose checks read failed for the same complete PR lands its discovery (clearing an older warning) and keeps the prior checks, reporting 'failed' once; equal numbers in another repository no longer count as the same PR. A viewed-file update from a departed observation changes nothing.

* Send the phone's addressed details reads with the server's read intent where it is advertised

github.workItemDetails accepts optional canonical:false (ordinary) / true (Refresh), advertised by github.exact-details-read-intent.v1; missing intent means ordinary. The phone's addressed details reads (PR tab and Tasks) now pass that intent through the existing exact-target preparation: a host advertising it gets canonical:false for ordinary loads, recovery and polls and canonical:true for the user's explicit Refresh; a canonical-only host gets ordinary reads without the owner and Refresh with the owner only; hosts with neither keep today's params, and an explicit Refresh there is refused as unsupported. Refresh intent travels only with the Refresh command, never as reconnect demand. The poll's checks-only settling moves into pr-sidebar-load-result to keep the controller within its size limit.

* Keep today's details params for a host with neither read-intent capability, Refresh included

* Drive the reviewer target contract through the new tab's reviewers sheet, first search included

* Cover a poll's discovery warning, its clearing, and a same-PR checks-only poll failure in the controller

* Prove the Source Control PR tab's demand, retained sheet and draft, failure line and fork/upstream request ownership on the new navigation

* Cover the details read intent the phone sends to explicit-intent, canonical-only and older hosts

* Fix the round's test typecheck and assertion findings: awaited controller loads, the diff review props, typed doubles

* Send Refresh's owner to a canonical-only server outside the exact-target check, as the desktop does

* Restore the base's mobile change-scope test, which the base merge dropped by mistake

* Prove the PR poller keeps each fork/upstream incarnation's poll, backoff and cadence its own

* Verify expected GitHub account at repository execution capture

* Give the Linear detail recording adapter the detail Refresh request its loader reads

* Keep the detail Refresh request ref out of the recorded owner projection, like the other ref

* Count cancelled GitHub probes against the desktop request cap main introduced

* Keep the comment tree out of the diff review controller test, as its drawers already are

* Restore the Source Control action icons the scenario rewrite dropped from its icon mock

* Supply the settled-list setter main added to the Tasks list fixture

* Preserve captured GitHub account expectations through client dispatch

* Re-record the Linear team-context goldens for this PR's actionItem owner effect

* Retain observed runtime status after switching environments

* Fix account contract fixture types and RPC setup timing

* Restore the negative-capability switch probe assertion

* Align account fixtures with runtime identity and configured origin

* Consolidate details test imports

* Type the reviewer test's search stand-in locally so native declarations stay out of its Node-checked program

* Add the PR tab Retry label's English entry the localization check requires

* Restore the PR tab Retry label's translations in the other mobile locales

* Pass the phone write reread through the reviewer contract.

The sheet fixture omitted the required reread, so the phone typecheck failed. Desktop settlement tests that main already removed were still on this branch and pulled telemetry into the web typecheck.

* Declare the phone pull request refresh failure.

The sidebar already shows this sentence when a refresh fails. The English catalog omitted it, and the boot bundle still carried two strings no startup call needs.

* Let the phone sidebar tests subscribe to connection state.

The sidebar records the client's connection before it loads a pull request. These tests drive that through the screen's connection prop, so the stand-in client only has to accept the subscription.

* Justify the phone sidebar test client.

The changed-code check rejects a new cast unless the line says why it is sound. These tests still only subscribe and read connection state.

* Name the repository on the phone sidebar details fixture.

A same-numbered pull request in another repository is not this one. The loaded body has to name the repository the test selected.

* Keep phone review-thread feedback on its accepted detail revision

* Capture phone review actions from the current render

* Align phone recording fixtures with the integrated detail loader

* Keep provider-switch detail fixtures addressed to the selected item

* Preserve same-turn bridge refusal evidence across client call shapes

* Publish phone detail revisions through one synchronous owner

* Type the queued publication regression with its provider contract

* Conform phone regression and relay types to static analysis

* Trim duplicate phone RPC tests and focus recorded observations
2026-11-03 14:19:08 -08:00
Kelvin Amoaba 14819eb8c0 fix(chat): isolate sends from terminal drafts (#37774)
* fix(chat): isolate sends from terminal drafts

* test(chat): expect replacement clear for resume commands

* test(chat): refresh draft-clear RPC recordings
2026-11-03 18:28:10 +00:00