Skip to content

Extract platform-neutral client logic into @bb/client-core - #1986

Merged
SawyerHood merged 2 commits into
mainfrom
bb-mobile-1-client-core
Aug 20, 2026
Merged

Extract platform-neutral client logic into @bb/client-core#1986
SawyerHood merged 2 commits into
mainfrom
bb-mobile-1-client-core

Conversation

@SawyerHood

@SawyerHood SawyerHood commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

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

Move the pure, DOM-free pieces of the web app (route paths, sidebar
grouping/ordering/read state, prompt drafts and mention triggers, timeline
merge/paging helpers, thread activity/runtime status, fixed-panel tab
state, file-preview and localhost-link policy, terminal websocket
transport, API types) into a new `packages/client-core` workspace package
so a native client can share them with `apps/app`.

Every moved module keeps a one-line re-export shim at its old path in
`apps/app`, so web behavior and imports are unchanged; the moved tests now
run under `@bb/client-core`, plus a guard test that the package never
touches DOM globals. `apps/app` gains the `@bb/client-core` dependency.

Co-Authored-By: Claude <noreply@anthropic.com>
@SawyerHood
SawyerHood force-pushed the bb-mobile-1-client-core branch from f1df923 to 536bdda Compare August 20, 2026 01:07

@bb-slop-cop bb-slop-cop Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 SLOP COP 🚨 · review

Plain-English summary: This pull request moves shared client rules into a new package. Mobile and web clients can then use the same rules.

I am SlopCop, and I have started the review. I will check security, code quality, performance, architecture, duplicate code, and the main user path.

I will post one final review after these checks finish.

- TerminalWebSocketTransport.start() now records `started` before the
  suspended early-return, so suspend() -> start() -> resume() opens the
  socket instead of leaving the transport idle forever (the mobile
  terminal hit this when the app backgrounded before the WebView posted
  "ready"). Test added for that ordering.
- The no-dom guard now also catches `globalThis.<global>`, `self.<global>`,
  optional chaining, `as Window`, and bare location/history/matchMedia/
  requestAnimationFrame references.

Co-Authored-By: Claude <noreply@anthropic.com>
@SawyerHood
SawyerHood marked this pull request as ready for review August 20, 2026 02:49
@SawyerHood
SawyerHood merged commit d678d65 into main Aug 20, 2026
12 checks passed
@SawyerHood
SawyerHood deleted the bb-mobile-1-client-core branch August 20, 2026 03:12
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 added a commit that referenced this pull request Aug 20, 2026
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.

- Connectivity: server profiles in SecureStore; Direct mode (LAN /
Tailscale / simulator) and bb connect (machine enrollment by QR/code →
desktop-session cookie in the native jar, renewal, re-pair); per-profile
SDK + realtime manager (ping/pong, watermark reconnect, AppState
suspend/resume) + QueryClient with realtime invalidation; global
mutation error toasts.
- Screens: drawer sidebar + home thread list (organize/sort, pinned,
unread glyphs, long-press actions), search, archived, compose with
pickers (project, provider, model+reasoning, permission mode, service
tier, environment incl. reuse/worktree/branch/path, machine), new
project + project settings + machine setup, thread detail (FlashList
timeline with every row kind via a renderer registry, native markdown,
native diffs, ANSI, lightbox, unread divider, ToC, prompt-stack cards),
prompt area (composer with mentions/typeahead/attachments/voice/drafts,
send/queue/steer/stop, queued messages, pending-interaction banner incl.
approvals, questions, ask-user-question and secret-request forms,
context banner with git/PR/parent/children, message/thread/git action
sheets, fork/handoff), workspace panel (Info, Diff, Files + previews,
terminal = bundled xterm WebView with RN-owned socket + accessory bar,
synced thread tabs), settings (general, appearance palettes from
`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.
- Design system: generated native theme tokens
(`scripts/generate-native-theme.ts` from `theme.css`, drift-tested),
primitives (sheets, action sheets, toasts, etc.), Inter + Fira Code,
hugeicons.
- Toolchain: Metro resolves `@bb/*` from TS source (`source` condition
scoped to workspace packages), `pnpm patch` for
`expo-modules-jsi@57.0.4` on Xcode 26.2, scoped `lightningcss` override
for NativeWind v5, turbo/eslint/prettier wiring,
`.github/workflows/mobile-e2e.yml` (label `mobile-e2e` / nightly;
Release build + Maestro) and `mobile-runner-probe.yml`, `eas.json`
profiles, docs (`docs/platform-support.md`,
`docs/repository-overview.md`).
- Early access: the bb-side pairing surfaces (Settings → Remote access →
Add mobile device, `bb connect machine-code`) are behind the `mobileApp`
experiment from #1987; the app's own Settings → Experiments screen lists
that toggle too (it is shared server state).
- Not in this layer: push notifications (#1989). Not yet: Android,
plugin frontends (SPA-in-WebView planned), inbound share (needs a native
rebuild), TestFlight (needs an EAS account).

## How you verified

- `pnpm exec turbo run typecheck lint test --filter=@bb/mobile` — 794
tests / 117 files, 0 lint findings.
- Every phase was built by parallel agents and independently
re-verified; the full Maestro regression (26 flows) passed on the iPhone
17 Pro simulator (iOS 26.3) against the seeded harness backend
(`tests/integration/mobile-e2e/backend.ts`) and the connect stub gate;
results table and screenshots list in `plans/bb-mobile-progress.md`.
Connect enrollment was also exercised against the real `bee.getbb.app`.
- On this layer specifically (no push): `phase1-shell`, `phase4b-send`,
`phase5-links` flows re-run and pass.
- Known gap carried in the log: on bb connect profiles the left-edge
drawer swipe can also press the home row under the touch (Direct
profiles are fine).

> AGENT GENERATED: by Claude Opus 5

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant