feat(mobile): share target with a share gate for new and existing sessions - #4779
Conversation
|
(bot) Standin review of the full PR diff at head This review was produced by a dedicated reviewer agent (fresh Checks the reviewer ran (all green): Findings (ordered by severity)1. Major — silent share-content loss via "New session" → "Run on" remote instance
2. Medium — second share can be silently dropped by the ingest effect
3. Medium — staged attachments can be silently omitted from the wire at send
4. Low — share to a read-only destination strands the consumed payload in a disabled composer
5. Low — cache-copied shared files are never deleted
6. Nit — 7. Nit — Verified contract points (no findings)
Verdict: changes-required (finding 1 is a reachable silent-loss path; findings 2–3 should also be addressed before merge). Triage and repairs are in progress; outcomes (fixes and any rejections with evidence) will be posted as follow-up comments. |
|
(bot) Triage outcome for the standin review posted above. Repairs landed in FixedFinding 1 (Major) — "Run on" remote instance swallowing a staged share. Now blocked for the whole staged lifetime, not just while Finding 2 (Medium) — second share dropped by the ingest effect. The Finding 3 (Medium) — staged files silently omitted at send. Finding 5 (Low) — cache-copied files never deleted. Rejected (with evidence)Finding 4 (Low) — read-only destination strands the payload. This is plan-documented known-expected behavior (Batch H plan assumption 7): read-only-with-messages sessions mount no composer, so a delivered share is never consumed from the store and remains intact; the zero-message read-only case mounts a disabled composer where the staged content stays visible and recoverable. Reachability is additionally narrowed by the new CLI admission gate ( Finding 6 (Nit) — Finding 7 (Nit) — Local checks on the repair head from |
|
(bot) Device E2E update for the follow-up scope (CLI share destinations + silent-loss guards), iOS simulator, worktree-local stack. Round 1 (head
|
| Row | Result |
|---|---|
| OS share → gate delivery, warm (Safari URL → gate → New session → prefilled, nothing sent) | PASS |
| OS share → gate delivery, cold start | PASS |
| CLI destinations listed (live row hoisted with live marker; offline CLI rows present) | PASS |
| Files → live capable CLI | FAIL — root-caused to active-sessions-live.ts: WS snapshot/heartbeat merges rebuilt cached rows without capabilities, so a live CLI session advertising attachments: true read as incapable and the gate wrongly showed This session can't receive files |
| Text → live CLI (send delivered, CLI pane mirrored) | PASS |
Offline CLI tap → Session not connected alert, gate open, payload intact |
PASS |
| Run-on gate | BLOCKED (no advertised CLI instance in that environment) |
| Cloud-agent destination (via live session) | PASS |
| Second share while gate open (Y replaces X) | PASS |
| Stale-share deep link | PASS |
| Abandonment (no payload anywhere after dismiss) | PASS |
Cache hygiene (share-* files gone after abandon) |
PASS |
The round-1 failure was fixed in af87eeac9 (merges now carry capabilities — wire value wins when present, cached value preserved when omitted) and reviewed.
Round 2 (head 0e2703447) — all rows pass, one environment limitation
| Row | Result |
|---|---|
| Files → live CLI: admission + composer staging with attachment chip from first paint | PASS |
| Files → live CLI: upload + send end-to-end | BLOCKED — environment: presign returns 200 with a well-formed signed URL, but the R2 PUT is rejected 401 Unauthorized. Independently reproduced from the host: a presigned GET on a nonexistent key also 401s, and a nextjs restart with fresh env injection does not help — the shared local dev R2 credential for cloud-agent-attachments-dev is stale/mis-scoped at the source. Not product code: the API path, URL shape, and signed headers are correct, and this affects all local attachment uploads equally. |
Run-on gate (instance listed → pick → Can't share to a new remote session, selection unchanged, prefill intact) |
PASS |
| Warm smoke | PASS |
| Text → live CLI, delivered | PASS |
| Offline reject | PASS |
Android was attempted for this follow-up and is recorded as an environment limitation: the emulator (AVD kilo_pixel9_api35, -gpu host, locks cleared) failed to boot to adb visibility twice under current host load. The changes since the last full-matrix pass are platform-agnostic JS; native Android share plumbing is untouched by them. Prior Android evidence on this branch: Kilo appears in the OS share chooser, and a direct SEND opens the gate.
Share-extension end-to-end delivery into a remote CLI session will be exercised in staging/production where the R2 credentials are valid; locally everything up to the R2 boundary is verified.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryReviewed the incremental diff since the last review (commit Files Reviewed (4 files)
Previous Review Summary (commit ca833fe)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit ca833fe)Status: No Issues Found | Recommendation: Merge Executive SummaryReviewed the full mobile share-target feature diff (44 files, ~4,700 changed lines) across payload/navigation libraries, share-gate UI, CLI admission/active-sessions merging, app shell navigation, composer/session-detail wiring, and build config; found no high-confidence security, correctness, or memory-leak issues in changed lines. Files Reviewed (44 files)
Reviewed by claude-sonnet-5 · Input: 46 · Output: 9.2K · Cached: 1.2M Review guidance: REVIEW.md from base branch |
|
(bot) Standin review loop — final outcome. Round 3 ( Round 4 ( Round 5 ( Repair-loop record: every valid finding went implementer → fresh reviewer → commit; two findings were rejected with evidence earlier in this thread (read-only stranding per plan assumption 7; |
|
(bot) Standin review, final-head pass at 1. Medium — focused delivery drops the committed 2. Low — single-slot pending navigation drops a previously committed share. Commit A → gate dismisses → share B ingested and gate replaced → commit B before A delivers: 3. Low — Triage: all three verified valid against the code — repair wave dispatched. Outcomes will be posted here; a fresh standin review follows the push. |
…tinations, keep committed payloads on dismiss
|
(bot) Round-6 standin findings — repaired and closed. Repairs landed in
Regression E2E on the pushed head (iOS, real OS share sheet): 4/4 PASS — warm share → New session prefill (nothing sent), text delivered to the live CLI session (CLI pane mirrored), offline CLI reject unchanged, abandonment unchanged. Round 7 standin review over the full diff at |
What
How
expo-share-intent@6.1.1(the SDK 55 line), plus a small local wrapper config plugin for two concrete issues: upstream requires@expo/plistwithout declaring it (breaks under pnpm's isolated linker), andiosShareExtensionName: 'Kilo'collides with the main app target name, so the wrapper forces targetShareExtensionand rewrites the share-sheet display name toKilo(failing prebuild loudly if the rewrite ever misses).shareIdroute parameter end to end — no reactive store, no claim/generation state.+native-intentswallows the share deep link (initial ? '/' : null) so a warm share never yanks the user to root.capabilities.attachments— their schema carries no organization, so rendering them as rows would leak cross-org), capped at 30, cloud-agent and CLI.showAttachmentskeeps prefilled chips visible before the session resolves; the picker stays gated onattachmentsEnabled).Notable
pnpm-workspace.yamlgainspackageExtensionsfor@sentry/react-nativeandexpo-share-intent: both have undeclared upstream dependencies that break Expo config evaluation outside the Expo CLI under pnpm (autoInstallPeers: false). This blocked the mandatory native rebuilds on both platforms (:expo-constants:createExpoConfig/ the EXConstants Xcode script phase). The failure class is pre-existing — it lives at the base commit — and is surfaced here because this feature requires real native builds.pnpx expo-doctorcurrently fails spawningexpo config --json --full; the failure reproduces at the base commit, so it is pre-existing and unrelated.npx expo configitself evaluates fine, and expo-doctor does not run in CI.kiloappscheme is verified in the generatedInfo.plist, and the generatedAndroidManifest.xmlcarriesACTION_SEND/ACTION_SEND_MULTIPLE.group.com.kilocode.kiloappmust be registered and enabled on both the app and extension identifiers.Tests
From
apps/mobile:pnpm format,pnpm typecheck,pnpm lint(0/0),pnpm check:unused,pnpm test(237 files / 2014 tests) all pass;git diff --checkclean. Unit coverage includes every gate feature state (selection logic and CTA presence/absence), the readiness predicate per clause, the payload store semantics, the navigation resolver, the destination selector (live hoist, cap, no cross-org rows), the CLI destination admission matrix (offline / no-file-capability / capable / text-only), the validated-hasFilesrule, the capabilities-preserving live-sync merge (upgrade / downgrade / legacy-absent on snapshot and heartbeat), the run-on share-staged latch and spawn-cancel predicates, the send-path silent-drop refusal, copy-failure surfacing (unreadablenotes), supersede/eviction cache cleanup, the preflight validator, and the prefill ordering/failure rules.Device E2E on iOS (real OS share sheet from Safari and Photos against a worktree-local stack, a live
kilo remoteCLI, and an advertised CLI instance): OS-share → gate delivery warm and cold; CLI destinations listed with live hoist; text delivered to a live CLI session (CLI pane mirrored); files admitted to a live attachment-capable CLI with the chip staged from first paint; offline-CLI and Run-on blocking alerts with payload intact; second share replacing the gate; stale-share; abandonment; cache hygiene — all pass. The files end-to-end upload leg is blocked locally by a stale shared dev R2 credential (401on the signed PUT, reproduced from the host; presign itself returns 200) — an environment limitation, not product code. Android: emulator failed to boot twice under host load (environment); the deltas since the last full-matrix run are platform-agnostic JS and the native Android share plumbing is untouched.