* fix(onboarding): clarify opencode-serve requirement and fail connect tests fast New users bounce at ~0% 7-day retention because nothing tells them the app needs a computer running `opencode serve` on the same network/Tailscale, and a bad IP hangs for the full 30s request timeout before failing. - Rewrite the no-connection empty state subtitle and add a "How to set up a server" link to the setup guide (app/(tabs)/index.tsx, src/lib/links.ts). - Surface the opencode-serve prerequisite as a one-line notice at the top of the Quick Connect form, above the existing detailed help box (app/connection/add.tsx). - Give the interactive connection test (testConnection) its own 12s timeout via an optional Client.global.health(timeoutMs) parameter, instead of reusing the general 30s REQUEST_TIMEOUT_MS used for real session traffic (src/lib/sdk.ts, src/stores/connections.ts). - Mirror all new/changed strings in the zh-Hans catalog; catalog-parity test keeps them in sync. * docs(distribution): add retention analysis motivating first-run fixes Diagnoses ~0% D7 retention as product-shape (no path to value without a self-hosted server, no demo mode, store copy sets no expectation). Ranks fixes and isolates the two owner-only strategic calls (store-copy honesty, hosted OpenCode Connect). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T12AhSnQVrSxNnvwfCx2z6 * fix(onboarding): drop connect-screen prerequisite notice (kept off-screen the submit button in E2E) The added notice pushed connect-submit-button below the fold, breaking the Maestro activation-positive flow (and the other flows sharing the connect prelude). The empty state already sets the opencode-serve expectation one screen earlier, so this notice was redundant. Empty-state guidance + guide link and the fast-fail connect timeout are unaffected and retained. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T12AhSnQVrSxNnvwfCx2z6 --------- Co-authored-by: engineer <engineer@macbookpro.lan> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
4.0 KiB
Retention Analysis — opencode-mobile
Last updated: 2026-07-17
The number that matters
- Installs: 1K+ (Google Play, live listing, package
cc.agentlabs.opencode). - 7-day retention: ~0% (per Notion "Retention Investigation").
Awareness is not the bottleneck anymore. Getting people to install works. Getting them to stay does not. Every hour spent on more distribution channels is spent on the wrong problem until activation is fixed.
Diagnosis: it's product-shape, not a bug
A brand-new installer's path (traced in code):
- Launch → telemetry consent modal first (
app/_layout.tsx:157-168) — a privacy prompt before the user has seen any value. - Land on Sessions tab, empty state: "No Connection / Add a server connection to get
started" (
app/(tabs)/index.tsx:421-440,src/lib/i18n/en.json:258-259). No hint that this needs a separate computer. - Tap Add → Quick Connect form asking for IP address + port + password
(
app/connection/add.tsx:225-407), hand-typed on a phone keyboard. - To get past this, the user must already have, on another machine: Node/npm,
opencode-aiinstalled,opencode serverunning, and LAN or Tailscale reachability. - A wrong IP hangs the spinner up to 30s before any feedback
(
REQUEST_TIMEOUT_MS=30_000,src/lib/sdk.ts:175).
There is no demo, sample, or try-it mode anywhere in the app. For any installer
without a self-hosted server already running — almost certainly the majority of casual
Play installs — the app is functionally inert after onboarding: empty state → a form
they can't fill → a "coming soon" mailing-list card (app/connection/add.tsx:343-397).
Nothing else to tap.
The store listing makes it worse: the short description ("AI coding agent in your pocket. Stream, diff, approve — free & open source.") sets zero expectation that a self-hosted server is required. People install expecting a standalone app, hit a wall, and uninstall. That is the ~0% D7 mechanism.
Fixes, ranked by impact ÷ effort
| # | Change | Impact | Effort | Owner-gated? |
|---|---|---|---|---|
| 1 | Set the self-hosting expectation in the empty state + connect screen; fail-fast timeout | High | Low | No — shipping via PR |
| 2 | One-screen "what you need" pre-flight with an "I don't have a server yet" branch | High | Med | No |
| 3 | QR-code pairing (server prints QR of URL+creds; app scans) — kills manual IP/port/password typos | High | Med | No |
| 4 | Local demo/sandbox session — scripted walkthrough of chat/diff/approve, no server needed | High | Med-High | No |
| 5 | Store-copy honesty: move the server prerequisite into the short description + first screenshot | High (on D7) | Low | Yes — Play Console |
| 6 | Hosted "OpenCode Connect" so users need no server at all (the real fix) | Highest | High | Yes — strategic/infra |
Items 1–4 are agent-shippable. Item 1 is in flight (branch
fix/first-run-onboarding-clarity).
The one decision only the owner can make
Do we optimize for install count or for activated users?
- Item 5 (put "needs a self-hosted server" in the store's short description) will reduce installs — it filters out people who can never activate. It will raise retention and store rating quality. This is a positioning tradeoff, not an engineering one, so it is the owner's call, not an agent's. Recommended: yes, be honest up front — a 4-star app with 300 real users beats a 2-star app with 1,000 bouncing ones.
- Item 6 (hosted OpenCode Connect) is the only change that removes the server prerequisite entirely. The waitlist card already in the app signals intent. This is the actual long-term retention fix and deserves a roadmap decision.
What this loop is doing about it
- Shipping items 1 (and scoping 2) as reviewable PRs.
- Not touching the live Play listing (item 5) or infra (item 6) — surfacing them here for the owner instead of acting unilaterally on positioning/strategy.