Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c7d744e
docs: add Sentry integration plan
claude May 1, 2026
fd33ffc
docs(sentry-plan): config plugin, native telemetry, capture toggle
claude May 1, 2026
0892aea
feat(bench): UDS / RPC bridge benchmark suite
claude May 1, 2026
7f8f047
fix(bench): Android variant resolution + Expo SDK pinning
gmaclennan May 1, 2026
e7b9a52
fix(bench): iOS resource ordering + shutdown race + Copilot review
claude May 1, 2026
5acd807
Merge remote-tracking branch 'origin/main' into claude/benchmark-uds-…
gmaclennan May 3, 2026
214d9b0
feat(module): add comapeoBackendDir override hook
gmaclennan May 5, 2026
97ea28d
refactor(bench): move bench backend + plugin into apps/benchmark
gmaclennan May 5, 2026
634b3db
refactor(module): drop bench-specific build wiring
gmaclennan May 5, 2026
c1bd188
refactor(api): rename benchMessagePort to unstable_messagePort
gmaclennan May 5, 2026
d6ff5f6
refactor(backend): revert rollup.config.ts to main
gmaclennan May 5, 2026
aa56b90
docs: drop sentry-integration-plan.md from this branch
gmaclennan May 5, 2026
944bdf7
fix(bench): create Resources PBXGroup before adding folder ref
gmaclennan May 5, 2026
080295e
docs: drop benchmark plan; refresh App.tsx header
gmaclennan May 5, 2026
33118dd
docs: add bench app README
gmaclennan May 5, 2026
94b4e53
feat(bench): wire BrowserStack runner + host-side span receiver
gmaclennan May 5, 2026
9063023
feat(bench): make BrowserStack project sticky; scaffold RESULTS.md
gmaclennan May 5, 2026
b11140e
docs(bench): clarify BENCH_BROWSERSTACK_PROJECT is exceptional
gmaclennan May 5, 2026
e64053b
fix(bench): correct BrowserStack execute path + enable device logs
gmaclennan May 5, 2026
7c08575
feat(module): add comapeoStubRootKey override for keystoreless devices
gmaclennan May 5, 2026
235fdbb
feat(bench): wire BS Local + cleartext fetch; first real-run results
gmaclennan May 5, 2026
190ca17
feat(bench): wire backend args + multi-device runner + summarizer
gmaclennan May 6, 2026
c5e70a7
refactor(bench): move Maestro flows under apps/benchmark/.maestro/
gmaclennan May 6, 2026
1190e01
refactor(bench): switch span transport from HTTP receiver to logcat
gmaclennan May 6, 2026
6548b9b
feat(bench): runner — Maestro 2.0.7, log-pull, auto-batch, 10-device …
gmaclennan May 6, 2026
7c69745
docs(bench): update README + RESULTS for log-based transport
gmaclennan May 6, 2026
93dd41f
feat(bench): iOS parity — pipe nodejs-mobile stdout to os_log + fix R…
gmaclennan May 6, 2026
b1efb15
feat(bench): add ios:archive script for BS-ready Development IPA
gmaclennan May 6, 2026
95787eb
fix(bench): route RN-side spans through ingestSpans RPC, fix summarizer
gmaclennan May 6, 2026
f6a3da6
docs(bench): clean up stale receiver/HttpSink references
gmaclennan May 6, 2026
d5fbcf5
refactor(bench): drop HttpSink, bench-receiver, ios maestro flow
gmaclennan May 6, 2026
fc3905d
feat(bench): tag backend rpc spans, surface bridge overhead in summary
gmaclennan May 6, 2026
50e1bad
fix(ios): gate nodejs-mobile stdout→os_log redirect behind plist flag
gmaclennan May 6, 2026
a6c277c
docs: plan for manual-trigger benchmark CI with artifact storage
gmaclennan May 6, 2026
e054e91
refactor(bench): tighten module override surface for the benchmark app
gmaclennan May 7, 2026
5b6381a
docs: tighten code comments across the bench branch
gmaclennan May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# BrowserStack App Automate credentials. Copy this file to .env and
# fill in the values from `Account → Settings → Access Keys` in the
# BrowserStack dashboard. Both scripts in scripts/ read these via
# `node --env-file=.env`.
BROWSERSTACK_USERNAME=
BROWSERSTACK_ACCESS_KEY=

# Optional escape hatch: pin builds to an existing BrowserStack
# project rather than letting BS auto-create one from the bundle ID.
# Leave unset for the normal path — the runner sends no `project`
# field and BrowserStack auto-creates a project named after the
# uploaded app's bundle ID. Only set this if your access key lacks
# project-creation permission AND you want to attach builds to a
# pre-existing project. Use the project name as it appears in the
# App Automate dashboard.
# BENCH_BROWSERSTACK_PROJECT=

# Apple Developer Team ID (10-char identifier). Required by
# `apps/benchmark/scripts/build-ipa.sh` to archive the bench app for
# BrowserStack iOS dispatches. Visible at the top right of any page on
# https://developer.apple.com/account/. The bundle id
# `com.comapeo.core.benchmark` must already be registered as an
# Identifier under the same team. No Capabilities or App Store Connect
# entry are required — BrowserStack auto-resigns IPAs with their own
# provisioning profile on upload.
# APPLE_DEVELOPMENT_TEAM_ID=ABC1234DEF

16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,19 @@ build/

# eslint
.eslintcache

# Local secrets (BrowserStack credentials etc). The .example file is
# checked in and lists the variables; copy it to .env and fill in.
.env
.env.local
.env.*.local

# Bench result NDJSONs, written by `scripts/bench-summarize.ts` after
# pulling device logs from a BrowserStack build. Treat as build
# artifacts; copy out of this dir if you want to commit a specific
# run's results.
apps/benchmark/results/

# `apps/benchmark/scripts/build-ipa.sh` output (xcarchive + IPA staged
# for BrowserStack upload). Regenerable; never commit.
apps/benchmark/ios-build/
14 changes: 11 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ def comapeoAbiFilters = {
return effective
}()

// Entry filename inside `nodejs-project/`. Default `index.mjs` (the
// production bundle). Consumers shipping a sibling entry file (e.g.
// `apps/benchmark/`) drop it into their own `assets/nodejs-project/`
// and override here; AGP merges the assets at APK build time.
def comapeoEntryFile = (rootProject.findProperty('comapeoEntryFile') ?: project.findProperty('comapeoEntryFile') ?: 'index.mjs').toString()

def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
Expand Down Expand Up @@ -76,6 +82,8 @@ android {
versionCode 1
versionName "0.1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// Java-string-literal form (embedded quotes) is what `buildConfigField` needs.
buildConfigField "String", "COMAPEO_ENTRY_FILE", "\"${comapeoEntryFile}\""
externalNativeBuild {
cmake {
cppFlags ''
Expand All @@ -87,6 +95,7 @@ android {
abiFilters(*comapeoAbiFilters)
}
}

lintOptions {
abortOnError false
}
Expand All @@ -98,9 +107,6 @@ android {
// native module instance × ABI). Both directories ship into the
// same APK `lib/<abi>/` segment.
jniLibs.srcDirs 'libnode/bin/', 'src/main/jniLibs/'
assets {
srcDirs 'src/main/assets'
}
}
}
externalNativeBuild {
Expand All @@ -111,6 +117,8 @@ android {
}
buildFeatures {
prefab true
// AGP 8.x default is off; needed for `buildConfigField` above.
buildConfig true
}
packagingOptions {
excludes += [
Expand Down
23 changes: 13 additions & 10 deletions android/src/main/java/com/comapeo/core/NodeJSService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ private data class ErrorNativeMessage(
const val APK_LAST_UPDATE_TIME_KEY = "apk_last_update_time"
const val SHARED_PREFS_NAME_POSTFIX = "_nodejs_preferences"
const val NODEJS_PROJECT_DIRNAME = "nodejs-project"
const val NODEJS_PROJECT_INDEX_FILENAME = "index.mjs"
// Override via `comapeoEntryFile` Gradle property; see android/build.gradle.
val NODEJS_PROJECT_INDEX_FILENAME: String = BuildConfig.COMAPEO_ENTRY_FILE

/**
* Bound on `ipcDeferred.await()` inside `sendErrorNativeFrame`. If the
Expand Down Expand Up @@ -409,15 +410,17 @@ class NodeJSService(
}
}

val exitCode = startNodeWithArguments(
arrayOf(
"node",
jsFile.absolutePath,
comapeoSocketFile.absolutePath,
controlSocketFile.absolutePath,
dataDir,
)
)
// `--device=<tag>` is read by downstream telemetry (bench, Sentry).
// Production backend ignores unknown flags so it's a safe no-op.
val deviceTag = "${android.os.Build.MANUFACTURER} ${android.os.Build.MODEL} (Android ${android.os.Build.VERSION.RELEASE})"
val exitCode = startNodeWithArguments(arrayOf(
"node",
jsFile.absolutePath,
comapeoSocketFile.absolutePath,
controlSocketFile.absolutePath,
dataDir,
"--device=$deviceTag",
))
log("NodeJS service completed with exit code $exitCode")

// Classify the exit. "Requested" means we asked for it
Expand Down
40 changes: 40 additions & 0 deletions apps/benchmark/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# dependencies
node_modules/

# Expo
.expo/
dist/
web-build/
expo-env.d.ts

# Native (regenerated by `expo prebuild`; the with-comapeo-bench config
# plugin re-applies bench wiring on every prebuild, so checking these
# in would only invite drift)
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision

# Metro
.metro-health-check*

# debug
npm-debug.*
yarn-debug.*
yarn-error.*

# macOS
.DS_Store
*.pem

# local env files
.env*.local

# typescript
*.tsbuildinfo

# expo prebuild
/android
/ios
28 changes: 28 additions & 0 deletions apps/benchmark/.maestro/bench-payload-1KB.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
appId: com.comapeo.core.benchmark
---
# Per-payload-size variant of bench-rpc.yaml: only 1KB selected.

- launchApp:
clearState: true

- extendedWaitUntil:
visible:
id: "service-state"
text: "STARTED"
timeout: 60000

- tapOn:
id: "size-64"
- tapOn:
id: "size-65536"

- tapOn:
id: "send-button"

- extendedWaitUntil:
visible:
id: "benchmark-result"
timeout: 60000

- assertVisible:
text: "1KB"
35 changes: 35 additions & 0 deletions apps/benchmark/.maestro/bench-payload-1MB.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
appId: com.comapeo.core.benchmark
---
# Per-payload-size variant of bench-rpc.yaml: only 1MB selected. Note
# 1MB is NOT in the default selection — the flow deselects the three
# defaults and selects 1MB explicitly. Timeout is bumped because each
# round-trip moves a megabyte through the bridge.

- launchApp:
clearState: true

- extendedWaitUntil:
visible:
id: "service-state"
text: "STARTED"
timeout: 60000

- tapOn:
id: "size-64"
- tapOn:
id: "size-1024"
- tapOn:
id: "size-65536"
- tapOn:
id: "size-1048576"

- tapOn:
id: "send-button"

- extendedWaitUntil:
visible:
id: "benchmark-result"
timeout: 180000

- assertVisible:
text: "1MB"
30 changes: 30 additions & 0 deletions apps/benchmark/.maestro/bench-payload-64B.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
appId: com.comapeo.core.benchmark
---
# Per-payload-size variant of bench-rpc.yaml: only 64B selected.
# Default selection is {64B, 1KB, 64KB}; deselect the two larger sizes
# so the run records 64B-only RTT distribution.

- launchApp:
clearState: true

- extendedWaitUntil:
visible:
id: "service-state"
text: "STARTED"
timeout: 60000

- tapOn:
id: "size-1024"
- tapOn:
id: "size-65536"

- tapOn:
id: "send-button"

- extendedWaitUntil:
visible:
id: "benchmark-result"
timeout: 60000

- assertVisible:
text: "64B"
28 changes: 28 additions & 0 deletions apps/benchmark/.maestro/bench-payload-64KB.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
appId: com.comapeo.core.benchmark
---
# Per-payload-size variant of bench-rpc.yaml: only 64KB selected.

- launchApp:
clearState: true

- extendedWaitUntil:
visible:
id: "service-state"
text: "STARTED"
timeout: 60000

- tapOn:
id: "size-64"
- tapOn:
id: "size-1024"

- tapOn:
id: "send-button"

- extendedWaitUntil:
visible:
id: "benchmark-result"
timeout: 90000

- assertVisible:
text: "64KB"
34 changes: 34 additions & 0 deletions apps/benchmark/.maestro/bench-rpc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
appId: com.comapeo.core.benchmark
---
# UDS / RPC bridge benchmark — bench app launches its own bench backend
# (rolled up from `apps/benchmark/backend/`, dropped into the consumer
# app's `nodejs-project/` by the `with-comapeo-bench` Expo plugin and
# loaded via the module's `comapeoEntryFile` override), waits for the
# service to reach STARTED, runs a sweep across the default payload
# sizes, and reads back the on-screen p50/p95/p99 panel. Spans are also
# written to the app's documents directory; "Export results" opens the
# system share sheet.

- launchApp:
clearState: true

- extendedWaitUntil:
visible:
id: "service-state"
text: "STARTED"
timeout: 60000

- tapOn:
id: "send-button"

- extendedWaitUntil:
visible:
id: "benchmark-result"
timeout: 120000

- assertVisible:
id: "benchmark-result"
- assertVisible:
text: "p50"
- assertVisible:
text: "p99"
12 changes: 12 additions & 0 deletions apps/benchmark/.maestro/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Workspace config for the bench Maestro suite. Picked up automatically
# when Maestro runs against this directory; the BrowserStack runner zips
# everything under here into a single parent folder so BS sees the same
# layout.
#
# `flows` constrains discovery to bench-only YAMLs — keeps stray editor
# files (`.swp`, `.DS_Store`) and accidental sibling additions out of the
# uploaded zip. The runner's `bench-summarize.ts` and the BS dispatch
# script don't read this file directly; it's purely for Maestro's CLI
# `maestro test` discovery.
flows:
- "bench-*.yaml"
Loading
Loading