Add the bb mobile app (Expo / React Native, iOS first) - #1988
Merged
Conversation
This was referenced Aug 20, 2026
SawyerHood
force-pushed
the
bb-mobile-3-app
branch
from
August 20, 2026 01:07
8fc3fd3 to
6ef703b
Compare
There was a problem hiding this comment.
🚨 SLOP COP 🚨 · review
I am the SlopCop. I started a review of this mobile application pull request.
I will review security, code quality, performance, architecture, duplication, and the available end-to-end paths. I will post the combined findings when the review finishes.
SawyerHood
force-pushed
the
bb-mobile-3-app
branch
2 times, most recently
from
August 20, 2026 02:12
17b4caf to
1b2321f
Compare
SawyerHood
force-pushed
the
bb-mobile-3-app
branch
from
August 20, 2026 02:47
1b2321f to
63e8dad
Compare
SawyerHood
marked this pull request as ready for review
August 20, 2026 02:49
SawyerHood
added a commit
that referenced
this pull request
Aug 20, 2026
Stack layer 1 of 4 for the bb mobile app (stack #1990). No prerequisite; the next layer is #1987. ## What was wrong The native mobile app (`apps/mobile`, layer 3) needs the web app's platform-neutral client logic — sidebar grouping and sorting, thread read state, prompt draft and submission policy, mention triggers, timeline merge/paging helpers, renderable-patch rules, the terminal WebSocket transport, panel tab schemas, file preview, localhost link rewriting, route builders. Today these live inside `apps/app/src` next to DOM and react-router code, so a React Native app cannot import them. ## What changed - New `packages/client-core` (`@bb/client-core`): the pure modules moved out of `apps/app` unchanged, plus a `test/no-dom.test.ts` guard that walks `src/**` and fails on `window`/`document`/`localStorage`/`navigator`/react-router/`@/` references. - `apps/app`: every old path is a one-line re-export shim, so web imports and tests keep working; the few files that mixed DOM and pure code (`route-paths.ts`, `fixed-panel-tabs-state.ts`, `useThreadTimelineController.ts`, `localhost-link-rewrite-preference.ts`, `terminal-websocket-url.ts`, `TimelineFileDiffBlock.tsx`, `secondaryPanelTabState.ts`) keep the web-only parts and import the rest from client-core. 19 tests moved with their modules. - `TerminalWebSocketTransport` treats an undefined `socket.bufferedAmount` as 0 (React Native's WebSocket never sets it), with a regression test. - `@bb/client-core` depends on `@bb/domain`, `@bb/server-contract`, `@bb/core-ui`, `@bb/thread-view`, `@bb/fuzzy-match`, `@bb/desktop-contract` (pure), `zod`. No React. No behavior change in the web app is intended. ## How you verified - `pnpm exec turbo run typecheck lint test --filter=@bb/client-core --filter=@bb/app` — client-core 236 tests, app 2955 tests / 392 files, lint 0 errors (the 144 warnings pre-exist on main). - `packages/client-core/test/no-dom.test.ts` guards the DOM-free rule; the transport test fails without the `bufferedAmount` fix. Part of the bb mobile app plan (`plans/bb-mobile-expo.md`, lands in #1988). > AGENT GENERATED: by Claude Opus 5 --------- Co-authored-by: Claude <noreply@anthropic.com>
SawyerHood
added a commit
that referenced
this pull request
Aug 20, 2026
Stack layer 2 of 4 for the bb mobile app (stack #1990). Prerequisite: #1986 (`@bb/client-core`). Next layer: #1988 (the app). ## What was wrong A native client needs a few small, additive pieces the server, cloud, SDK, and test harness do not have today: a way to tell the server it is a mobile client, SDK types that survive React Native's global `Response`/`FormData` declarations, a shared contract for the two plugin-rendered pending interactions so the phone can render them natively, a deterministic approval path in the fake provider for e2e, a harness that can run on a fixed port behind a stub gate, a way to mint a connect pairing code for a phone, and app-link association files so universal links can work. ## What changed - `packages/config` + `apps/server`: request-side `RequestAppSurface` (`desktop | web | mobile`) for `x-bb-app-surface` + telemetry; the server config union stays `desktop | web`. Test added. - `packages/sdk`: `SdkResponseLike` structural constraint in `response.ts`/`transport.ts` (Hono client responses stay assignable under RN globals); `projects.sidebarBootstrap()`; public-types test updates. - New `packages/plugin-interaction-contracts`: the ask-user-question and secret-request payload/resolution schemas moved from the two plugins' private `contracts.ts`; both plugins re-export them (their tests unchanged). - `packages/agent-runtime` fake provider: `approve:<kind>` control token (`command | file_change | permission_grant | plan`) emits an approval interactive request; adapter decodes/encodes approval payloads; 5 tests. - `tests/integration`: harness `serverPort`/`bindHost` options; `mobile-e2e/backend.ts` (seeded long-lived harness) and `connect-stub.ts` (TLS stub gate in front of the harness) + scripts + turbo tasks. - `plugins/connect`: "Add mobile device" QR/code in Settings → Remote access and `bb connect machine-code [--json]` (uses the existing `createMachineCode` RPC; 409 machine-limit surfaced). Guide template, bb-cli SKILL, `docs/multiple-devices.md`, `docs/configuration.md` pairing section updated per `docs/cli-guide-and-skill.md`. - **Hidden behind the new `mobileApp` experiment** (off by default; Settings → Experiments → "Mobile app", or `bb settings experiment mobileApp true`) until the app is generally available. `@bb/domain` gains the key; the connect plugin reads it through its loopback SDK (`bb.sdk.system.config()`) on every call — a new `mobilePairing` rpc tells the panel whether to render the section, and `createMachineCode` / `bb connect machine-code` refuse with a pointer to the toggle. No new plugin-SDK API. With the experiment off, a paired install sees no visual change from this PR. - `packages/connect-client`: pairing helpers used by the phone. - `apps/connect` (gate) + `apps/web` (apex) + `packages/connect-db`: serve `/.well-known/apple-app-site-association` and `/.well-known/assetlinks.json` before the session gate (static JSON only; Android fingerprints from `ASSETLINKS_SHA256_FINGERPRINTS`, empty if unset). No auth change. No daemon contract change → `HOST_DAEMON_PROTOCOL_VERSION` untouched. ## How you verified - `pnpm exec turbo run typecheck lint` for config, server, sdk, plugin-interaction-contracts, ask-user-question, secrets, connect plugin, agent-runtime, integration-tests, connect-client, templates, @bb/connect, @bb/web, connect-db, cli — pass (the only failure is the pre-existing `conversation-outline-parity.test.ts` typecheck on main from #1657). - Tests: sdk 96, agent-runtime 413, connect plugin 88, templates 43, connect/connect-db/connect-client, server telemetry + public route tests (342), `@bb/integration-tests test:smoke` 28 — all pass. Part of the bb mobile app plan (`plans/bb-mobile-expo.md`, lands in #1988). > AGENT GENERATED: by Claude Opus 5 --------- Co-authored-by: Claude <noreply@anthropic.com>
SawyerHood
force-pushed
the
bb-mobile-3-app
branch
from
August 20, 2026 03:12
b28bb0e to
56be125
Compare
`apps/mobile`: a native phone client for a bb server over Direct URLs or bb connect pairing. Home thread list + drawer, search, archived, compose with pickers / mentions / attachments, the thread screen (timeline with every row kind, unread divider, table of contents; send / queue / steer / stop; approvals, questions, plugin forms; message / thread / git action sheets; fork / handoff; context banner), deep links (`bb://` + universal links), the workspace panel (Info / Diff / Files / Terminal), and the settings screens (General / Appearance / Experiments / Haptics, providers, usage limits, Machines, Updates, Plugins, Skills). Built on `@bb/client-core`, `@bb/sdk/browser`, `@bb/thread-view`, and the pending-interaction contracts. Toolchain: the `expo-modules-jsi` Xcode 26.2 patch + pnpm overrides, mobile eslint rules and ignores, prettier ignores, `@bb/mobile#dev` / `#test` turbo tasks, the `Mobile E2E` and `Mobile Runner Probe` workflows (Maestro flows on the iOS simulator against the integration harness backend), the platform-support and repository-overview docs, and the mobile plan / progress / research notes. Push notifications (the Expo push client, Settings → Notifications rows, and the server side) arrive in the next PR of this stack; the flows and READMEs here note where they plug in. Co-Authored-By: Claude <noreply@anthropic.com>
Mirror the new `mobileApp` toggle (added on the core-prep layer) in the app's Settings → Experiments list, include the key in the Phase 7 e2e settings reset (PUT /settings/experiments is exhaustive under zod 4), and note the gate in the README and platform docs. Co-Authored-By: Claude <noreply@anthropic.com>
- Files: project-file HTML previews show source like the web; only the CSP-sandboxed raw routes load in the preview WebView (docstring states the invariant; tests added) - Connection: replace literal NUL bytes with escapes so the connector and Markdown.tsx diff as text; add a circuit breaker to the connect-mode auth-failure -> re-mint -> refetch cycle (cap, error state, cooldown) - Realtime bridge: mirror the web registry — no PR refetch on file-watcher work-status/git-refs changes, PR refresh on turn/completed, thread record only on metadata/background-activity changes; evict the observer-less diff patch cache on reconnect - Session: the desktop-session cookie domain must domain-match the server host before it is installed (iOS only substring-checks) - Dev spike routes redirect home unless e2e/dev mode; `bb://dev/*` and `bb://e2e/*` scheme links resolve to `/` in release bundles - Markdown links: allow-list http(s)/mailto/irc(s)/xmpp like the web; other schemes are inert - Machines/Updates: stop using the phone's compiled daemon protocol version as the server's; read it from GET /install/version - Thread composer mounts once the thread record is loaded, so drafts are never keyed on a placeholder project id - Root .prettierignore skips Expo prebuild output; Maestro installs from a pinned, checksummed release zip instead of `curl | bash` Co-Authored-By: Claude <noreply@anthropic.com>
`expo run:ios` requires a development signing identity whenever the entitlements contain `com.apple.developer.associated-domains` — even for a simulator build — so the first Mobile E2E run failed with "No code signing certificates are available to use" once the app claimed universal links. Build with `--device generic --output build-output` (generic iOS Simulator destination, no signing setup) and install the product with `simctl`. DerivedData stays in its default location, so both native caches still hit. Co-Authored-By: Claude <noreply@anthropic.com>
The health-check loop used `kill -0 $!`, but `$!` is the `pnpm exec turbo` wrapper: on the macOS runner it exits while turbo and the backend keep running, so the loop declared "backend exited early" one tick before the server answered /health (the uploaded backend.log shows it had printed "ready" and was only stopped by the cleanup step). Give up only when the wrapper and the `mobile-e2e/backend.ts` process are both gone, and fail explicitly when /health never answers in the window. Co-Authored-By: Claude <noreply@anthropic.com>
SawyerHood
force-pushed
the
bb-mobile-3-app
branch
from
August 20, 2026 03:13
56be125 to
980c95b
Compare
Two problems stacked on top of each other. The server answers /health as soon as it listens, before it has seeded the project and the four threads the flows drive, so /health is not a readiness signal. And the details JSON line that *is* the signal never reached the log, because turbo groups task output in CI and only flushes it when the task ends — which a persistent task never does. Stream turbo's output and wait for that line (verified locally with CI env set: it appears ~26s in, while the log stayed empty with grouped output). Co-Authored-By: Claude <noreply@anthropic.com>
SawyerHood
added a commit
that referenced
this pull request
Aug 20, 2026
…obile (#1995) ## What was wrong #1993 removed the `claudeCodeMockCliTraffic` experiment key from `@bb/domain` while #1988 (the mobile app) was in flight. After both merged, `main` fails `@bb/mobile` typecheck: `apps/mobile/src/screens/settings/ExperimentsSettingsScreen.tsx` still lists the key, and `apps/mobile/e2e/scripts/phase7-settings-reset.js` still PUTs it to `/api/v1/settings/experiments` (the experiments record is exhaustive under zod 4, so the reset would 400 and the nightly Mobile E2E would fail). ## What changed - Removed the row from the mobile Experiments screen. - Removed the key from the e2e settings-reset payload. ## How you verified - `pnpm exec turbo run typecheck lint test --filter=@bb/mobile` on `main` + this change: all green (119 test files). Typecheck fails on `main` without it. - `git grep claudeCodeMockCliTraffic` leaves only historical migration/test references. > AGENT GENERATED: by Claude Opus 5 Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack layer 3 of 4 for the bb mobile app (stack #1990). Prerequisites: #1986 (
@bb/client-core) and #1987 (core prep). Next layer: #1989 (push notifications).What was wrong
bb has no native phone client. The PWA works on a phone but is bounded by mobile-web limits (keyboard/viewport hacks, no push, no background reconnect, no secure credential storage, no camera/photo attachments, WebKit style-recalc stalls). Plan, decisions, and research:
plans/bb-mobile-expo.md,plans/bb-mobile-research/; execution log with per-phase verification:plans/bb-mobile-progress.md.What changed
New
apps/mobile(@bb/mobile, Expo SDK 57 / React Native 0.86 / Expo Router / NativeWind v5, iOS first; ~81k lines of app code, ~20k of unit tests, 26 Maestro flows). Everything else in this PR is toolchain/docs; no server or web behavior changes.theme.css, experiments, providers, usage, machines list/detail/add, updates, plugins management + marketplaces, skills library/registry, haptics), deep links (bb://, universal link mapping, thread-open signal), outbound share.scripts/generate-native-theme.tsfromtheme.css, drift-tested), primitives (sheets, action sheets, toasts, etc.), Inter + Fira Code, hugeicons.@bb/*from TS source (sourcecondition scoped to workspace packages),pnpm patchforexpo-modules-jsi@57.0.4on Xcode 26.2, scopedlightningcssoverride for NativeWind v5, turbo/eslint/prettier wiring,.github/workflows/mobile-e2e.yml(labelmobile-e2e/ nightly; Release build + Maestro) andmobile-runner-probe.yml,eas.jsonprofiles, docs (docs/platform-support.md,docs/repository-overview.md).bb connect machine-code) are behind themobileAppexperiment from Prepare core, cloud, and test surfaces for the bb mobile app #1987; the app's own Settings → Experiments screen lists that toggle too (it is shared server state).How you verified
pnpm exec turbo run typecheck lint test --filter=@bb/mobile— 794 tests / 117 files, 0 lint findings.tests/integration/mobile-e2e/backend.ts) and the connect stub gate; results table and screenshots list inplans/bb-mobile-progress.md. Connect enrollment was also exercised against the realbee.getbb.app.phase1-shell,phase4b-send,phase5-linksflows re-run and pass.