Skip to content

refactor(mobile): one new-session screen for every entry point - #4945

Open
iscekic wants to merge 62 commits into
mainfrom
new-session-github-16aa
Open

refactor(mobile): one new-session screen for every entry point#4945
iscekic wants to merge 62 commits into
mainfrom
new-session-github-16aa

Conversation

@iscekic

@iscekic iscekic commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

What

  • Use one create-session screen for cloud and remote CLI targets.
  • Let the share gate choose New session or an existing destination.
  • Keep the S1 composer for remote CLI and deliver its content after spawn.

Why

A remote CLI spawn now carries the composer text and files to its destination.

This removes the remote-share loss case and keeps one configuration screen.

How

  • share-submit-params.ts snapshots the trimmed draft and local attachment URIs.
  • share-navigation.ts adds reusable delivery query parameters.
  • use-remote-spawn-dispatch.ts stages the press-time snapshot on ready navigation.
  • use-new-session-share-remote.ts supplies the S1 text and attachments.
  • new-session-configure-form.tsx keeps the prompt, mode, and model controls for a remote target.
  • new.tsx settles voice input before both cloud and remote submission paths.
  • share-prefill.ts signals delivery after files queue and clears both delivery parameters.
  • composer-auto-send.ts gates a one-shot destination send on delivery, connection, upload, and attachment capability.
  • [session-id].tsx, session-detail-content.tsx, and chat-composer.tsx forward and consume autoSend.

Verified, unchanged

R1 — one create-session route

  • session-list-screen.tsx:269 and :318 push getNewAgentSessionPath(...).
  • new-task-button.tsx:25 and agents-promo-card.tsx:25 push the same path.
  • share-gate-sheet.tsx:226 pushes getNewAgentSessionPath(...).
  • session-list-routes.ts:1 resolves each path to /(app)/agent-chat/new.
  • agent-chat/ has new.tsx, [session-id].tsx, and picker routes only.

R2 — existing destination delivery

  • share-gate-sheet.tsx:222 routes New session with shareId; new-session-prompt.tsx:135 applies the prefill.
  • share-gate-sheet.tsx:234 routes a stored destination with shareId; chat-composer.tsx:261 applies the prefill.
  • share-destinations.ts:20 includes cloud-agent and CLI rows and hoists live rows.
  • Navigation only commits the payload. It does not send it.

Removed

  • Removed share-to-new-remote-session.ts, use-share-aware-run-on-change.ts, use-share-staged-latch.ts, and ready-navigation cancellation.

The remote-share guard blocked a remote Run on target while a share was staged, and the
ready-navigation cancel stopped a spawn from navigating away with shared content still on the screen.
Both existed because a spawned remote session could not receive the shared text and files. The spawn
now carries the composer content to the new session, so neither has a case left to protect.

Verification

  • pnpm format — passed.
  • pnpm typecheck — passed.
  • pnpm lint — passed.
  • pnpm check:unused — passed.
  • pnpm test -- --run — 300 files and 2,645 tests passed.
  • pnpm run test:mobile-workflow — 161 tests passed.
  • git diff --check — passed.
  • Cumulative implementation review — no findings.

iOS bot E2E

  • Scope: iOS only. The change has no platform-specific code.
  • E1 — passed. Home and Agents open the same S1 screen.
  • E2 — passed. The real share gate listed New session and stored sessions; S1 showed unsent shared text.
  • E3 — passed. A development GitHub installation created a live cloud-agent session on iscekic/eslint-plugin-zod-utils with Auto Efficient. A real Safari share selected its live row and left the shared text unsent in the destination composer.
  • E4 — passed. Safari share to a live CLI session opened an unsent composer. The CLI transcript had no shared message.
  • E5 — passed. Remote selection kept hello from the s1 screen; the spawned session export contained it.
  • E6 — passed. A real Safari share survived CLI selection and reached the spawned CLI session.
  • E7 — passed. A real Photos share showed e7-tiny.PNG in the gate and S1. The connected CLI advertised attachment support. The spawned session held e7-tiny.PNG, uploaded and an unsent composer.

The continuation verifier restored its temporary Appium patch, stopped its local share server, and passed its baseline check.

Post-review repair

  • The delivery latch now arms only for non-empty shared text and disarms after a user edit.
  • Remote spawn requires explicit attachment capability for file payloads, matching the share gate.
  • iOS R1 passed: a shared image and auto send verification 4945 auto-sent to the spawned CLI session.
  • iOS R2 passed: a files-only spawn kept the image chip and the typed x unsent in the composer.

Visual Changes

E1 — one S1 screen

e1-home-s1.png

E2 — share gate

e6-gate.png

E5 — remote S1 start

e5-after-start.png

E6 — shared remote start

e6-after-start.png

E4 — share to a CLI destination

e4-dest.png

E3 — share to a cloud-agent destination

e3-after.png

E7 — shared image remote start

e7-spawned.png

R1 — repaired text and image auto-send

r1-spawned.png

R2 — files-only draft remains unsent

r2-after-type.png

C1 — capable CLI admits image payload

c1-spawned.png

@iscekic iscekic self-assigned this Jul 31, 2026
@iscekic

iscekic commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

(bot) E2E is blocked before verifier dispatch: no iOS simulator was unclaimed. The failed bundle's Android emulator, stack, recorders, and slot were released. No visual evidence was fabricated.

Comment thread apps/mobile/src/app/(app)/agent-chat/new.tsx Outdated
Comment thread apps/mobile/src/app/(app)/agent-chat/new.tsx Outdated
Comment thread apps/mobile/src/lib/agent-github-integration.ts
@kilo-code-bot

kilo-code-bot Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental range 4bee91b18..f5c0451cd fixes the previously flagged fail-open remote-spawn admission default; the gate now denies staged files unless the selected CLI explicitly advertises capabilities.attachments === true, matching the share gate, and no new issues were found.

Files Reviewed (2 files, incremental range 4bee91b..f5c0451)
  • apps/mobile/src/lib/remote-spawn-admission.ts
  • apps/mobile/src/lib/remote-spawn-admission.test.ts
Resolved since the previous review
  • Admission gate failed open on absent capabilities (remote-spawn-admission.ts:14) — fixed. capabilities?.attachments !== true now treats a missing capabilities object the same way share-gate-sheet.tsx and the share admission path do (=== true required), so a legacy or not-yet-heartbeated CLI no longer receives files it cannot accept. The added tests cover explicit false, absent, and explicit true, plus the null and text-only payload admit paths.
Verified as safe (checked and ruled out)
  • Early payload === null return is redundant with the subsequent optional access but behaviorally identical to the prior payload?.files.length guard; no path change.
  • Text-only and zero-file payloads still admit regardless of capability (files.length > 0 guard), so the stricter default does not block plain-text remote spawns.
  • Caller integration unchanged: use-remote-spawn-dispatch.ts:197-205 still reads the press-time snapshot before any await and returns before buildCreateRemoteSessionInput/spawn, so a rejection stages nothing and starts no session.
  • Type surface: InstancePickerInstance.capabilities remains optional (picker-bridge.ts:64); no construction site breaks and no new nullable dereference is introduced.
  • Memory leaks: no timers, subscriptions, listeners, or retained payloads added in this range.
Assumptions
  • Read-only sandbox: no tests, typecheck, lint, or build were executed; conclusions come from reading code at f5c0451cd.
  • Incremental scope is 4bee91b18..f5c0451cd (2 files, 29 changed lines). Findings in files unchanged in that range are not carried forward.
  • Denying files for an unknown-capability CLI is a deliberate consistency choice with the share gate; the stricter default can reject a legacy CLI that would in fact accept attachments, which is the intended conservative behavior.
Previous Review Summaries (5 snapshots, latest commit 4bee91b)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 4bee91b)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

The new remote-spawn admission gate only rejects files for a CLI that explicitly advertises attachments: false, so a CLI that advertises nothing still receives staged files it can never accept — the opposite default from the share gate.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/lib/remote-spawn-admission.ts 10 capabilities?.attachments === false fails open: a CLI whose heartbeat omits capabilities (legacy build, or first heartbeat not yet landed) is admitted with files, while share-gate-sheet.tsx:137,262 and share-cli-admission.ts treat absent capabilities as not capable (=== true). Same spawn-a-CLI-session action, opposite default. Tracked in the existing thread on use-remote-spawn-dispatch.ts; no duplicate inline comment posted.
Files Reviewed (7 files, incremental range a4af3a1..4bee91b)
  • apps/mobile/src/components/agents/chat-composer.tsx
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts
  • apps/mobile/src/lib/composer-auto-send.ts
  • apps/mobile/src/lib/composer-auto-send.test.ts
  • apps/mobile/src/lib/picker-bridge.ts
  • apps/mobile/src/lib/remote-spawn-admission.ts - 1 issue
  • apps/mobile/src/lib/remote-spawn-admission.test.ts
Resolved since the previous review
  • Auto-send latch never disarmed (chat-composer.tsx) — fixed in 4bee91b18. Arming now goes through shouldArmAutoSendOnDelivery, which requires non-empty delivered text, so an attachments-only carried payload never arms. applySharePrefill applies text through onChangeText before onDelivered (share-prefill.ts:46-65), so shareDeliveredRef is still false during delivery and the new handleChangeText disarm (chat-composer.tsx:276-278) fires only on genuine user edits, including the voice-draft path. autoSend=1 is produced only by the freshly spawned-session navigation (use-remote-spawn-dispatch.ts:222), so no pre-existing draft in an unrelated composer can be armed by a delivery.
Verified as safe (checked and ruled out)
  • Admission gate reachability: capabilities survives end to end — the tRPC row declares it (apps/web/src/routers/active-sessions-router.ts:43-58), instance-picker.tsx/instance-selector.tsx pass live object references through the module-level picker-bridge singleton with no JSON round trip, and instance-picker-rows.ts:39-46 spreads rather than rebuilds. The === false branch is reachable; only the absent-capabilities default is wrong.
  • No side effects lost on rejection: getSubmitPayload (share-submit-params.ts:buildComposerSharePayload) is a pure snapshot, so the early return after a failed admission stages nothing and leaks no cache copies or store entries.
  • onStart deps: the added admission call reads only runOnInstance (already a dependency) and the press-time payload ref; no new stale-snapshot window.
  • Test fixtures typecheck-compatible: remote-spawn-admission.test.ts file objects match AgentAttachmentCandidate (name, uri, optional mimeType/size) and include failedFiles.
  • Additive type change: the optional capabilities field on InstancePickerInstance cannot break existing construction sites.
  • Memory leaks: no new timers, subscriptions, listeners, or retained payload owners in this range; shareDeliveredRef is a plain per-mount ref.
Assumptions
  • Read-only sandbox: no tests, typecheck, lint, or build were executed; conclusions come from reading code at 4bee91b18.
  • Incremental scope is a4af3a15c..4bee91b18 (7 files, ~120 changed lines). Findings whose files are unchanged in that range — including the share-submit-params.ts localUri suggestion the author rejected — are not carried forward.
  • No new inline comment was posted: the one remaining finding is the same defect as the active thread on use-remote-spawn-dispatch.ts, where the author documented the narrowed gate as intentional.

Fix these issues in Kilo Cloud

Previous review (commit a4af3a1)

Status: 3 Issues Found | Recommendation: Address before merge

Executive Summary

The new auto-send latch in chat-composer.tsx is armed but never disarmed, so a remote start that carries attachments with no prompt turns the user's first keystroke into an unintended send.

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/components/agents/chat-composer.tsx 566 Auto-send latch never disarms; a text-less carried payload leaves it armed, so the first typed character (or removing a failed chip, or canSend flipping after a user edit) fires a send
apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts 211 Files are staged into a spawned CLI without the attachments-capability admission the share gate enforces; on a non-capable CLI nothing is ever sent and nothing explains why

SUGGESTION

File Line Issue
apps/mobile/src/lib/share-submit-params.ts 16 Carrying localUri re-uploads every attachment in the destination composer and orphans the first uploaded object
Files Reviewed (22 files, incremental range 261e954..a4af3a1)
  • apps/mobile/src/app/(app)/agent-chat/[session-id].tsx
  • apps/mobile/src/app/(app)/agent-chat/new.tsx
  • apps/mobile/src/components/agents/chat-composer.tsx - 1 issue
  • apps/mobile/src/components/agents/new-session-configure-form.test.ts
  • apps/mobile/src/components/agents/new-session-configure-form.tsx
  • apps/mobile/src/components/agents/session-detail-content.tsx
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.test.ts
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts - 1 issue
  • apps/mobile/src/components/share/share-gate-sheet.tsx
  • apps/mobile/src/lib/composer-auto-send.test.ts
  • apps/mobile/src/lib/composer-auto-send.ts
  • apps/mobile/src/lib/share-navigation.test.ts
  • apps/mobile/src/lib/share-navigation.ts
  • apps/mobile/src/lib/share-prefill.test.ts
  • apps/mobile/src/lib/share-prefill.ts
  • apps/mobile/src/lib/share-submit-params.test.ts
  • apps/mobile/src/lib/share-submit-params.ts - 1 issue
  • apps/mobile/src/lib/use-new-session-share-remote.ts
  • Deleted: share-to-new-remote-session.ts, use-share-aware-run-on-change.ts, use-share-staged-latch.ts (+ tests)
Verified as safe (checked and ruled out)
  • Auto-send racing uploads: addCandidates commits status: 'pending' in the same batch as setShareDelivered, and isAnyAttachmentUploading counts pending and uploading, so the first commit that sees shareDelivered also sees isUploading.
  • Double submit: autoSendFiredRef is set synchronously before void submitRef.current(), and settleVoiceInputBeforeSubmit owns the single SubmitLock.
  • autoSend replay / disarm: onDelivered() precedes clearShareIdParam(), the payload store is read-and-delete, and router.replace removes the new-session screen from the stack, so a stale autoSend=1 URL cannot re-arm.
  • Double spawn from repeated taps: useRemoteInstanceSpawn.spawn sets inFlight synchronously before its first await, so isSpawningRemote covers the window the submission lock releases early. The two residual windows are identical to pre-diff behavior.
  • Stale snapshot / hook deps: onStart's dependency array is complete, getSubmitPayload is read through a ref, and promptRef/attachmentsRef are written on every mutation path.
  • localUri validity at the destination: nothing deletes the cache copy after upload; takeSharePayload deliberately retains files.
  • Route-staged share double-consumed: the original id is taken once and a new id is minted from live composer state; nothing is stranded.
  • Deleted modules: repo-wide search finds no residual imports or references, including apps/mobile/e2e.
  • Memory leaks: no new timers, subscriptions, or listeners. The staged remote payload has no explicit owner, but the store is FIFO-capped at SHARE_PAYLOAD_MAX_ENTRIES with cache-file cleanup on eviction.
Assumptions
  • Read-only sandbox: no tests, typecheck, lint, or build were executed; all conclusions come from reading code at a4af3a15c.
  • Findings carried by the previous summary are not repeated: they were anchored in base-branch E2E harness content that this PR does not change, and every earlier in-scope inline finding already has an author reply.
  • Review scope is the incremental range 261e954c1..a4af3a15c; unchanged files were not re-analyzed.

Fix these issues in Kilo Cloud

Previous review (commit 261e954)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The two commits added since the previous review (53422e065, 261e954c1) are empty retrigger commits, so there is no new code to review, and every previously reported finding is either already answered in an existing thread or belongs to base-branch content that is not changed by this PR.

Files Reviewed (21 files)
  • apps/mobile/src/app/(app)/agent-chat/new.tsx
  • apps/mobile/src/components/agents/new-session-cloud-form.tsx
  • apps/mobile/src/components/agents/new-session-configure-form.test.ts
  • apps/mobile/src/components/agents/new-session-configure-form.tsx
  • apps/mobile/src/components/agents/new-session-model-provider.tsx
  • apps/mobile/src/components/agents/new-session-prompt-initial-prompt.test.ts
  • apps/mobile/src/components/agents/new-session-prompt.tsx
  • apps/mobile/src/components/agents/new-session-repository-section.tsx
  • apps/mobile/src/components/agents/new-session-repository-state.test.ts
  • apps/mobile/src/components/agents/new-session-repository-state.ts
  • apps/mobile/src/components/agents/remote-spawn-composer.tsx
  • apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts
  • apps/mobile/src/lib/agent-github-integration.test.ts
  • apps/mobile/src/lib/agent-github-integration.ts
  • apps/mobile/src/lib/is-repository-section-visible.test.ts
  • apps/mobile/src/lib/is-repository-section-visible.ts
  • apps/mobile/src/lib/use-github-repos-refresh-helpers.ts
  • apps/mobile/src/lib/use-github-repos-refresh.test.ts
  • apps/mobile/src/lib/use-github-repos-refresh.ts
  • apps/mobile/src/lib/use-new-session-repos.ts
  • apps/mobile/src/lib/use-new-session-share-remote.ts
Scope reconciliation
  • Incremental range 88a921ca0..261e954c1 is empty: both new commits (53422e065, 261e954c1, both titled chore: retrigger review) contain no file changes. git diff --stat --find-renames over that range produces no output.
  • HEAD re-confirmed as 261e954c1 immediately before this write.
  • The PR's changed-file list against its base (mobile-e2e-ste @ 96200a3e0, which is also the merge base) contains only the 21 apps/mobile/src/** paths above. git diff 96200a3e0...261e954c1 -- apps/mobile/e2e dev/local dev/seed is empty, so the #4960 harness (apps/mobile/e2e/appium.sh, apps/mobile/e2e/github-stub.sh, dev/local/mobile-android.ts, dev/local/mobile-simulator.ts, dev/seed/app/github-integration-copy.ts, and the related tests and flows) is byte-identical to the base branch and is not changed by this PR.
  • Consequently the four WARNING and two SUGGESTION findings carried by the previous summary are dropped: all six were anchored in that unchanged harness content, which is why they could not be posted inline (422 Path could not be resolved for every path). They are base-branch concerns, not changes introduced by this PR, and are out of scope under changed-lines-only review.
  • All 64 existing inline threads were re-read from the API with full pagination. Every bot finding has an author reply describing a fix or an explicit rejection, and none of the in-scope apps/mobile/src/** findings reproduces on current HEAD.
Verification notes and assumptions
  • No new inline comments were posted this round, because there are no new or unresolved in-scope findings; per incremental review rules, unchanged files were not re-analyzed and no previous finding was carried forward.
  • Read-only sandbox: no tests, builds, typechecks, formatters, or linters were executed. Verification was by GitHub API reads and local git range comparison only.
  • Assumption: CI on 261e954c1 covers the mobile unit suites for the 21 changed files; that result was not inspected from this session.

Previous review (commit 88a921c)

Status: 6 Issues Found | Recommendation: Address before merge

Executive Summary

The restored #4960 harness brings back two state-handling defects that can orphan a live Appium server and bypass the .env.local "not ours" guard, plus a dev:seed copy whose synthetic github_user_id is derived from a non-unique normalized email and can crash on a second copy.

Overview

Severity Count
CRITICAL 0
WARNING 4
SUGGESTION 2

Inline anchoring failed. All six findings are reported here only. Two atomic POST /pulls/4945/reviews attempts (with and without commit_id) were rejected with 422 Path could not be resolved for every path, because the PR file list GitHub currently serves for this PR contains only the 21 apps/mobile/src/** paths and none of the apps/mobile/e2e/**, dev/local/**, or dev/seed/** paths restored by 975ff19c9. Line numbers below are current HEAD (88a921ca0) line numbers.

Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/e2e/appium.sh 136 appium.pid is written at spawn time but server.port only after the readiness loop (149/154/164). The adoption guard on line 81 requires both files, so a run killed in that window (external SIGTERM, harness timeout, or the set -e abort when stop_server returns 1 at line 170) leaves pid-only state that the next ensure_server ignores: it spawns a second server and line 136 overwrites the retained pid, so the live server's only handle is lost and server stop can never reap it. Write server.port next to the pid at spawn, or make the guard act on a lone appium.pid.
apps/mobile/e2e/github-stub.sh 164 The "not ours" refusal tests grep -q "$MARKER" file-wide instead of on the matched ^GITHUB_API_BASE_URL= line. A stale marker left by a SIGKILLed start therefore disables the guard even when the developer's own GITHUB_API_BASE_URL= line is genuinely not ours; remove_env_line strips only marker lines and line 232 appends a second definition of the same key, so which value wins depends on the consumer's dotenv parser. Scope the check to the matched line, e.g. grep '^GITHUB_API_BASE_URL=' "$ENV_LOCAL" | grep -qv "$MARKER".
dev/seed/app/github-integration-copy.ts 351-355 The synthetic github_user_id hashes normalizedEmail, but normalizeSeedEmail strips +tags and Gmail dots and this file's own target lookup notes normalized_email is not unique — so the comment's "deterministic per target" and "cannot collide" claims do not hold. Two accounts such as e2e+ios@example.com / e2e+android@example.com get the same id; the upsert on line 390 conflicts on (kilo_user_id, github_app_type), not on UQ_user_github_app_tokens_github_user_app (packages/db/src/schema.ts:4105), so the second copy raises an unhandled unique_violation and exits with a raw Postgres error instead of a guarded message. Hash the resolved target.id instead.
dev/local/mobile-android.ts 702 The already exists exemption does not cover Android emulator launch is locked by another live process (withProcessLockAsync in startAndroidEmulator, dev/local/process-lock.ts:52), which likewise means another live process owns the launch. Control falls through to the unlocked stopAndroidEmulator on line 709, which for a same-worktree race kills the winner's kilo-e2e-android-* tmux session (including on the ENOENT-record path), SIGTERM/SIGKILLs its emulator PID, and deletes its record. Cross-worktree the teardown is a no-op but the retry downgrades to swiftshader_indirect and contends again, reporting a spurious environment blocker.

SUGGESTION

File Line Issue
apps/mobile/e2e/github-stub.sh 47 KILO_STUB_LOCKED=1 is never unset after the guard on lines 43-48, so it is inherited by tmux new-session on line 200. When that command starts the tmux server, tmux copies the environment into the server's global environment, and a later github-stub.sh start|seed|stop run from any pane of that server takes the already-locked branch and runs with no lock at all. unset KILO_STUB_LOCKED after the guard (or env -u / tmux new-session -e) closes the leak.
apps/mobile/e2e/appium.sh 247 grep -c '<' counts matching lines, not elements, and wdio/hierarchy.js writes getPageSource() verbatim. Android UiAutomator2 returns the XML on one line, so a complete hierarchy prints as 1 elements — the number the runbook tells agents to read as proof the dump is usable. grep -o '<' "$OUT" | wc -l (or the byte size) cannot understate a healthy dump.
Files Reviewed (23 files changed since afd64f0)
  • apps/mobile/e2e/appium.sh - 2 issues
  • apps/mobile/e2e/github-stub.sh - 2 issues
  • dev/seed/app/github-integration-copy.ts - 1 issue
  • dev/local/mobile-android.ts - 1 issue
  • apps/mobile/src/app/(app)/agent-chat/new.tsx - 0 issues
  • apps/mobile/src/components/agents/new-session-configure-form.tsx - 0 issues
  • apps/mobile/src/components/agents/new-session-configure-form.test.ts - 0 issues
  • apps/mobile/src/components/agents/new-session-flow-body.tsx (deleted) - 0 issues
  • apps/mobile/src/components/agents/new-session-flow-body.test.ts (deleted) - 0 issues
  • apps/mobile/src/components/agents/run-target-step.tsx (deleted) - 0 issues
  • apps/mobile/src/lib/new-session-flow-state.ts (deleted) - 0 issues
  • apps/mobile/src/lib/new-session-flow-state.test.ts (deleted) - 0 issues
  • apps/mobile/e2e/AGENTS.md - 0 issues
  • apps/mobile/e2e/record.sh - 0 issues
  • apps/mobile/e2e/login.sh - 0 issues
  • apps/mobile/e2e/logout.sh - 0 issues
  • apps/mobile/e2e/remote-cli.sh - 0 issues
  • apps/mobile/e2e/flows/open-app.js - 0 issues
  • apps/mobile/e2e/flows/settle-app.js - 0 issues
  • dev/local/mobile-simulator.ts - 0 issues
  • dev/local/mobile-simulator.test.ts - 0 issues
  • dev/local/mobile-workflow.test.ts - 0 issues
  • dev/local/tmux.ts - 0 issues
Resolved since the previous review
  • The five CRITICALs that flagged dev/local/appium-wrapper.test.ts and dev/local/mobile-workflow.test.ts as asserting a reverted harness are resolved: 975ff19c9 restores apps/mobile/e2e/appium.sh, github-stub.sh, flows/open-app.js, and flows/settle-app.js, and the two test files at HEAD are byte-identical to the pre-revert state those assertions were written against.
  • dev/seed/app/github-integration-copy.ts:82 (help text naming a nonexistent script) is resolved: the usage text now names apps/mobile/e2e/github-api-stub, which exists.
  • The two-step new-session flow is gone: new.tsx now renders one NewSessionConfigureForm for every entry point, which removes the flowMode === 'pending' skeleton latch and the showBackButton header regressions previously reported on that file.
Verification notes and assumptions
  • Incremental range afd64f039..88a921ca0 (3 commits, 23 files, ~2350 changed lines). HEAD re-confirmed as 88a921ca0 immediately before each write attempt.
  • git diff 96200a3e0 88a921ca0 -- apps/mobile/e2e dev/local dev/seed is empty, so 975ff19c9 restores the harness byte-for-byte to the last pre-revert state. Findings above are defects in that content that no earlier review round raised; every previously raised harness defect with an active thread was left alone and is not repeated here.
  • Refactor scope checked by search: no dangling references remain to new-session-flow-body, run-target-step, or new-session-flow-state; isShareStaged is still produced and consumed inside use-new-session-share-remote.ts / use-share-aware-run-on-change.ts. removeAttachment and retryAttachment are both (id: string) => void, so the new named wrappers in new.tsx introduce no floating promise.
  • Observation, not a finding: the restore brought back dev/seed/app/github-integration-copy.ts but not its unit test (dev/local/github-integration-copy.test.ts is absent at HEAD while dev/local/appium-wrapper.test.ts returned), so that module is now uncovered.
  • Read-only sandbox: no tests, builds, typechecks, formatters, or linters were executed. Verification was by reading files at HEAD, git range comparison, and repository search only.

Fix these issues in Kilo Cloud

Previous review (commit afd64f0)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental range 0381b0dd7..afd64f039 only reverts shared dev/e2e harness files back to their origin/main content and deletes the seed copy script plus its test, so no new risk is introduced.

Files Reviewed (5 files changed since 0381b0d)
  • dev/local/mobile-simulator.ts — reverted to base (atomic claim writes, bootstatus/shutdown timeouts, rollback shutdown hook removed)
  • dev/local/mobile-android.ts — reverted to base (exact =session tmux matching and emulator relaunch policy removed)
  • dev/local/tmux.ts — reverted to base (exact =session matching removed)
  • dev/seed/app/github-integration-copy.ts — deleted
  • dev/local/github-integration-copy.test.ts — deleted
Verification notes and assumptions
  • git diff origin/main HEAD over the three reverted dev/local files is empty, so their post-revert content is byte-identical to the merge base (b9ebddce3); a revert to base cannot regress relative to base, so no findings were raised on those lines.
  • No dangling references remain to the deleted seed module: repository-wid

[Snapshot truncated.]

Additional previous summary content was truncated to keep this comment within platform limits.


Reviewed by claude-opus-5 · Input: 30 · Output: 4.3K · Cached: 653.6K

Review guidance: REVIEW.md from base branch mobile-e2e-ste

Comment thread apps/mobile/src/app/(app)/agent-chat/new.tsx Outdated
iscekic added 2 commits August 1, 2026 02:28
Cherry-pick of cloud#4949 adb path only so Android E2E recorder works
before that PR merges.
@iscekic

iscekic commented Aug 1, 2026

Copy link
Copy Markdown
Contributor Author

(bot) BLOCKED: the required Android bot-E2E verdict is still unavailable. The record.sh adb repair on the current head passed a fresh Android recorder preflight, but three fresh verifier sessions each exited 0 without a required sentinel: one after recorder preflight, one after baseline/artifact setup, and one after successful Android login while preparing the narrow step/back case. This is verifier/tooling failure, not a product verdict. The plan requires Android for the platform-specific return path, so passed iOS evidence cannot substitute. Every recorder, emulator/simulator claim, remote CLI, stack, and E2E slot started by this run was released; no human-ready label was added. Full evidence: workflow final report retained in the section scratch.

@iscekic iscekic added the human-ready The PR is ready for human review. label Aug 1, 2026
iscekic added 13 commits August 2, 2026 00:16
…olerant flows

- rewrite e2e/AGENTS.md in simplified technical English: task-structured,
  scripts own the mechanics, 410 -> ~215 lines
- new e2e/github-stub.sh: PR-review stub server + env line + token seed
  as one reversible command pair; failed starts clean up after themselves
- emulator-start --wait retries a failed launch once itself (boot-envelope
  timeout keeps the GPU, other failures switch to software rendering);
  the runbook's manual GPU decision table is gone
- appium.sh hierarchy writes to a file and prints the path; a raw XML dump
  into an agent session is large enough to kill it silently
- raise iOS cold-launch/settle budgets to 120s (timed out under parallel
  dual-bundle load); document ANR-under-load handling
- simulator claims record originalDeviceName and bootedByClaim intent in
  the initial write, so a lost post-boot rewrite can no longer leak a
  renamed or still-booted device on release
- emulator-start retry: rethrow the already-exists guard (a retry would
  tear down a live emulator) and keep attempt 1's error when the interim
  teardown itself fails
- github-stub: curl transport failures now route through seed_fail, so a
  failed start still rolls back the session and env line
- github-stub: guard against a .env.local without a trailing newline
  before appending, so the marker line can never merge into (and later
  delete) an existing variable
- tmux targets use exact-match (=name): bare -t prefix-matches, so a sibling
  worktree whose slug extends ours could be probed or killed in its place
- simulator claims write atomically (temp + rename/link): in-place truncation
  let an unlocked reader see a partial file, judge it corrupt, and delete a
  live claim
- a claim abandoned after its boot (rename failure) now powers the device
  off, or keeps the claim when even that fails
- simctl bootstatus is bounded (15 min) so a wedged CoreSimulator cannot
  block a claim forever
- emulator-start tears down after a failed second attempt too
- github-stub start claims its port machine-globally and verifies its own
  session survived, so two worktrees cannot share one stub silently; stop
  releases the claim
- appium: driver install re-checks inside the install lock; server startup
  verifies port ownership before adopting a listener; stop escalates to
  SIGKILL instead of orphaning a live server
- record.sh serializes start/stop per device with the shared process lock
- open-app consumes its launch budget in 30s slices so an ANR dialog is
  answered promptly instead of after the full wait
- login.sh matches the recipient literally, not as a regex
- hierarchy mktemp template puts Xs last (BSD mktemp leaves embedded Xs
  literal, making every bare call after the first fail)
- AGENTS.md: per-platform mixed fixtures, pull-and-grep for ADB dumps,
  stub-stop token residue note
- mobile-workflow tests updated for the raised launch/settle budgets
- appium: the recorded-server fast path now verifies the recorded pid owns
  the listener before adopting (a recycled pid plus a sibling's server on
  the same port answered /status and got adopted)
- github-stub: seeding extracted into seed_token; new 'seed <email>'
  subcommand adds a token row for a second signed-in account (the other
  platform's verifier) without touching the running stub
- github-stub: the port claim covers only the choose-to-bind window and an
  EXIT trap releases it, so an interrupted start cannot leak claims and
  erode the port range
- github-stub: githubUserId carries the pid — concurrent seeds in the same
  second cannot collide on the unique github_user_id
- AGENTS.md: the ADB dump example creates its own mktemp file instead of
  assuming $SCRATCH
devSeedUserGithubToken can answer 200 with success:false (row not
upserted); the status code alone reported a seed that never landed.
A process that survives even SIGKILL kept its state file removed and the
stop reported success; the caller then released device claims over a live
server. stop now fails and keeps the state in that case.
- claim_port reaps an orphaned port claim (older than 120s with nothing
  listening) and the start path traps INT/TERM/HUP besides EXIT, so a
  killed start can no longer shrink the port pool permanently
- githubUserId derives deterministically from the account email: the
  upsert's setWhere only updates a matching row, so the fresh random id
  made every re-seed of an already-seeded account report success:false
  and tear the stub down with a misleading hint
- lsof ownership probes tolerate no-match exits; pipefail otherwise killed
  appium.sh before the fresh-start fallback could run
…ling

- appium startup adopts a port only on proven ownership (listener pid equals
  ours); an empty lsof keeps polling instead of adopting a vanished or
  foreign server
- github-stub signal traps exit instead of continuing a start that just
  released its port claim
- success:false with 200 and no error means an account seeded under an older
  random id — keep that usable row instead of tearing the stub down
success:false can come from an older random-id seed or from a real GitHub
connection on the dev account; the keep-message now says so instead of
implying only legacy seeds, and notes the stub accepts any token.
…lback

- a kept token row is verified to be the stub's (getUserAuthorization
  githubLogin == kilo-stub-user); a real GitHub connection row fails with a
  drop-the-row hint — under the stub its refresh bypasses
  GITHUB_API_BASE_URL, hits real github.com, and revokes itself
- start rolls back everything (session, env line, state, port claim) via one
  EXIT trap when it dies before the final state write, so an aborted start
  no longer blocks the next one behind 'session already runs'
- appium ownership checks degrade to pid+status when lsof is missing or
  cannot attribute the socket, instead of killing healthy servers across 50
  port blocks
- the row probe checks HTTP status and error envelope before advising: a
  failed probe reports 'could not determine the existing row' instead of
  confidently telling the operator to delete a row it never examined; a
  kept row must also be connected (a revoked stub row gets the drop hint)
- start's rollback undoes only what this run did (session-created and
  env-added flags), so a start losing the session-creation race cannot kill
  the winner's session, env line, or state
- the recorded-server reuse path applies the same blind-lsof rule as the
  start loop: adopt on pid+status after three blind probes instead of
  orphaning a healthy server and consuming a port block per invocation
- drop dead SEED_FAIL_MODE and the stale random-id comment
- github-stub creates its session under a pid-unique name armed before the
  create and renames to claim the canonical one: a signal at any instant
  tears down only our own session, and a lost rename race touches nothing
  but our port claim; the env-added flag arms before the append for the
  same reason
- the appium reuse probe rechecks pid liveness and /status on every pass,
  so a server dying mid-probe is cleaned up (stop_server) instead of
  adopted; a foreign listener still only drops our state
…windows

Concurrent stub commands raced over the session name, env line, and state
dir; each guard narrowed the window without closing it. The shared
process-lock (the appium.sh pattern) removes the class: one stub command per
worktree at a time, so the entry check is authoritative, the rollback flags
only order against signals, and the unique-name rename dance goes away.
Comment thread apps/mobile/e2e/appium.sh Outdated
Comment thread apps/mobile/e2e/appium.sh Outdated
Comment thread apps/mobile/e2e/appium.sh Outdated
Comment thread apps/mobile/e2e/appium.sh Outdated
Comment thread dev/local/appium-wrapper.test.ts Outdated
iscekic added 2 commits August 2, 2026 10:28
Two review corrections, both verified against dev/local/mobile-android.ts:
doctor ignores flags and always prints the whole environment (PATH
included), so the runbook's 'plain doctor is safe' advice pointed at the
unsafe form; and tail on JSON leaves an unparseable fragment while the
runbook requires parsing dev:status --json. The bounded form is a jq field
selector, shown inline for both cases.
Comment thread dev/local/appium-wrapper.test.ts Outdated
Comment thread dev/local/mobile-workflow.test.ts Outdated
Comment thread dev/local/mobile-workflow.test.ts Outdated
Comment thread dev/local/mobile-workflow.test.ts
Comment thread dev/local/mobile-workflow.test.ts
Comment thread dev/seed/app/github-integration-copy.ts
iscekic added 2 commits August 2, 2026 15:27
The e2e harness changes belong to #4960.
The assertions pin apps/mobile/e2e content that #4960 owns.
Point the seed help text at the stub that exists on main.
@iscekic
iscekic force-pushed the new-session-github-16aa branch from d1b3ff8 to 0381b0d Compare August 2, 2026 13:27
iscekic added 5 commits August 2, 2026 15:44
PR #4960 carries these changes and merges first. This branch keeps no copy.

- dev/local/mobile-android.ts
- dev/local/mobile-simulator.ts
- dev/local/tmux.ts
- dev/seed/app/github-integration-copy.ts

Drop dev/local/github-integration-copy.test.ts. It imports the reverted seed
file.
…-github-16aa

# Conflicts:
#	apps/mobile/e2e/AGENTS.md
@iscekic
iscekic changed the base branch from main to mobile-e2e-ste August 2, 2026 17:58
@iscekic iscekic changed the title fix(mobile): recover GitHub connect and add session steps refactor(mobile): one new-session screen for every entry point Aug 2, 2026
Comment thread apps/mobile/src/components/agents/chat-composer.tsx
Comment thread apps/mobile/src/components/agents/use-remote-spawn-dispatch.ts
Comment thread apps/mobile/src/lib/share-submit-params.ts
@iscekic iscekic removed the human-ready The PR is ready for human review. label Aug 2, 2026
@iscekic iscekic added the human-ready The PR is ready for human review. label Aug 2, 2026
Base automatically changed from mobile-e2e-ste to main August 3, 2026 01:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant