Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0f2557e
Strip upstream telemetry/support services
claude Oct 13, 2026
0c63001
fix(android): keep composer above keyboard
duoyi88 Aug 28, 2026
9b9a03f
fix(android): reset composer after keyboard hides
duoyi88 Aug 28, 2026
7377a8a
fix(android): keep IME prompts usable
Sep 21, 2026
71e63fe
test(android): cover scaled composer minimum
Sep 21, 2026
b526296
fix(android): keep connection form fields above keyboard
Sep 21, 2026
e1f2c29
fix: preserve session-specific chat drafts
chliny Aug 18, 2026
2d2b14c
fix(diagnostics): distinguish 401 with no credentials sent (auth-requ…
Dre4my26 Sep 27, 2026
b7df071
feat(chat): navigate previous and next message starts (#214)
Oct 1, 2026
dddbe51
fix(chat): reserve message navigation gutter (#214)
Oct 1, 2026
e935921
fix(chat): retain navigation intent across delayed scroll events (#214)
Oct 2, 2026
4b8def6
fix(chat): mirror Android inverted-list navigation gutter (#214)
Oct 2, 2026
978a52b
feat: Changes view per chat (branch / uncommitted / this turn)
claude Oct 13, 2026
065b7c6
feat: open a workspace from an opencode://connect link
claude Oct 13, 2026
e2a0ec7
feat: new chats start in the right repo
claude Oct 13, 2026
fc98328
Drop leftover waitlist/telemetry strings; add forge fork notes
claude Oct 13, 2026
025af16
Merge remote-tracking branch 'origin/main' into ccr-c14e7425-4ntke7
claude Oct 13, 2026
7f11378
Changes: always send the chat's directory; Branch tab only off defaul…
claude Oct 13, 2026
376a4b5
e2e: Maestro flows target it.kjeldahl.opencode, no consent step
claude Oct 13, 2026
6184a30
Revert "feat(chat): navigate previous and next message starts (#214)"
claude Oct 13, 2026
d7ce300
Changes: prev/next file arrows in the diff header
claude Oct 13, 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
2 changes: 1 addition & 1 deletion .github/workflows/activation-e2e.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Activation E2E (Maestro)

# Deterministic regression coverage for the activation flow (first open ->
# telemetry consent -> server URL entry -> connect -> send first message),
# server URL entry -> connect -> send first message),
# including the connect-time-401 negative case tied to the 0%-7-day-retention
# / GitHub issue #76 investigation.
#
Expand Down
190 changes: 2 additions & 188 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,6 @@ jobs:

build:
runs-on: ubuntu-latest
env:
EXPO_PUBLIC_SENTRY_DSN: ${{ secrets.EXPO_PUBLIC_SENTRY_DSN }}
EXPO_PUBLIC_POSTHOG_KEY: ${{ secrets.EXPO_PUBLIC_POSTHOG_KEY }}
EXPO_PUBLIC_CHATWOOT_INBOX_IDENTIFIER: ${{ secrets.EXPO_PUBLIC_CHATWOOT_INBOX_IDENTIFIER }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
# No Sentry token (forks): skip the source-map upload, which otherwise fails the Gradle build.
SENTRY_DISABLE_AUTO_UPLOAD: ${{ secrets.SENTRY_AUTH_TOKEN == '' && 'true' || 'false' }}
steps:
- uses: actions/checkout@v6

Expand Down Expand Up @@ -73,20 +64,6 @@ jobs:
- name: Install dependencies
run: npm install --legacy-peer-deps

- name: Set Sentry release identifiers
# sentry.gradle (applied from android/app/build.gradle) defaults the
# upload's --release/--dist to `${applicationId}@${versionName}+${versionCode}`,
# which does NOT match the release/dist Sentry.init() reports at runtime
# (`opencode-mobile@${app.json version}`, see src/lib/sentry.ts). That
# mismatch made every uploaded source map land under a release Sentry
# never looks up, so symbolication silently failed. Pin the Gradle-side
# values to exactly what the app reports.
run: |
VERSION=$(node -p "require('./app.json').expo.version")
echo "SENTRY_RELEASE=opencode-mobile@${VERSION}" >> "$GITHUB_ENV"
echo "SENTRY_DIST=${VERSION}" >> "$GITHUB_ENV"
echo "Sentry release=opencode-mobile@${VERSION} dist=${VERSION}"

- name: Expo prebuild
run: npx expo prebuild --platform android --no-install

Expand Down Expand Up @@ -137,165 +114,8 @@ jobs:
name: app-release
path: android/app/build/outputs/apk/release/app-release.apk

# F-Droid rebuilds this app from source using the recipe in
# distribution/fdroid-submission/metadata.yml (Builds:), which applies
# fdroid/expo-application-patches and fdroid/expo-notifications-patches to
# strip Firebase Cloud Messaging / Play Install Referrer / Sentry before
# compiling. If we only ever publish the full-featured `app-release.apk`
# (built above, unpatched) and metadata.yml's `Binaries:` points at it,
# F-Droid's reproducible-build check compares its from-source (patched)
# rebuild against that (unpatched) reference binary and can never match —
# see issue #95.
#
# Fix: build a SECOND, additional artifact here — app-release-fdroid.apk —
# by applying the exact same patch set F-Droid's own recipe applies, then
# publish it as an extra GitHub Release asset. metadata.yml's `Binaries:`
# points at THIS asset, not app-release.apk. The main `build` job above is
# completely untouched: Play/GitHub/IzzyOnDroid users keep the full-featured
# binary (push notifications, Sentry crash reporting) unchanged.
#
# This job intentionally does NOT run `npx expo prebuild` — F-Droid's own
# Builds: recipe doesn't either; it patches the already-committed `android/`
# tree directly. Running prebuild here would regenerate android/app/build.gradle
# and could diverge from what F-Droid's build server produces from the same
# tracked source, defeating the byte-for-byte parity this job exists for.
#
# This job also intentionally receives NONE of the EXPO_PUBLIC_SENTRY_DSN /
# EXPO_PUBLIC_POSTHOG_KEY / EXPO_PUBLIC_CHATWOOT_INBOX_IDENTIFIER / SENTRY_*
# secrets that the main `build` job sets — F-Droid's isolated build
# environment has no access to this repo's secrets either, so baking any of
# them into the JS bundle here would itself be a source of reproducible-build
# divergence.
build-fdroid:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: actions/setup-node@v6
with:
node-version: 20
cache: npm

# F-Droid's metadata.yml prebuild patch bumps the Kotlin/Java toolchain
# target from 17 to 21 (see the "Apply F-Droid source patches" step
# below). Install both so Gradle's toolchain auto-detection can resolve
# either, matching F-Droid's own multi-JDK build environment.
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17

- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 21

- name: Setup Android SDK
uses: android-actions/setup-android@v4

- name: Cache Gradle
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
android/.gradle
key: ${{ runner.os }}-gradle-fdroid-${{ hashFiles('android/**/*.gradle*', 'android/gradle/wrapper/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-fdroid-

- name: Install dependencies
run: npm install --legacy-peer-deps

- name: Apply F-Droid source patches
# Mirrors distribution/fdroid-submission/metadata.yml `Builds:.prebuild`
# verbatim so this CI build and F-Droid's from-source rebuild patch the
# exact same lines/files. Keep these two in sync on any future change.
run: |
set -euxo pipefail

# Drop the Sentry native gradle plugin apply (no SENTRY_* secrets
# are available in F-Droid's build environment either).
sed -i '/apply from.*sentry.gradle/d' android/app/build.gradle

# F-Droid's build environment toolchains on JDK 21; bump the
# Kotlin/Java compile target used by RN's gradle plugin + expo-modules-core.
sed -i '/jvmToolchain\|JavaVersion/s/17/21/' \
node_modules/@react-native/gradle-plugin/*/build.gradle.kts \
node_modules/@react-native/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/JdkConfiguratorUtils.kt \
node_modules/expo-modules-core/android/ExpoModulesCorePlugin.gradle
printf '\nkotlin.jvm.target.validation.mode=warning\n' >> android/gradle.properties

# Strip Firebase Cloud Messaging from expo-notifications.
sed -i '/firebase/d' node_modules/expo-notifications/android/build.gradle
cp -a fdroid/expo-notifications-patches/. \
node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/
rm -f \
node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/RemoteMessageSerializer.java \
node_modules/expo-notifications/android/src/main/java/expo/modules/notifications/notifications/model/triggers/FirebaseNotificationTrigger.kt

# Strip Google Play Install Referrer (GMS-only API) from expo-application.
sed -i '/installreferrer/d' node_modules/expo-application/android/build.gradle
cp -a fdroid/expo-application-patches/. \
node_modules/expo-application/android/src/main/java/expo/modules/application/

- name: Setup signing
run: |
if [[ -n "${{ secrets.KEYSTORE_BASE64 }}" ]]; then
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/release.keystore
echo "RELEASE_STORE_FILE=release.keystore" >> "$GITHUB_ENV"
echo "RELEASE_STORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> "$GITHUB_ENV"
echo "RELEASE_KEY_ALIAS=${{ secrets.KEY_ALIAS }}" >> "$GITHUB_ENV"
echo "RELEASE_KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}" >> "$GITHUB_ENV"
# Same production keystore as app-release.apk: F-Droid's
# AllowedAPKSigningKeys check requires this binary to carry the
# same signing certificate fingerprint.
echo "Signing: production keystore"
else
keytool -genkey -v -keystore android/app/debug.keystore -storepass android \
-alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000 \
-dname "CN=Android Debug,O=Android,C=US"
echo "Signing: debug keystore (non-release build)"
fi

- name: Build F-Droid-flavored APK
working-directory: android
run: ./gradlew assembleRelease

- name: Re-sign APK v1+v2 only (drop v3/v4 for fdroidserver compatibility)
if: ${{ env.RELEASE_STORE_FILE != '' }}
run: |
# Same rationale as publish-fdroid.yml: androguard (used by
# fdroidserver) crashes parsing a v2+v3 signature block pair. Force
# v1+v2-only here deterministically.
APK=android/app/build/outputs/apk/release/app-release.apk
APKSIGNER=$(ls "$ANDROID_HOME"/build-tools/*/apksigner | sort -V | tail -1)
echo "Using $APKSIGNER"
"$APKSIGNER" sign \
--ks android/app/release.keystore \
--ks-pass "pass:${RELEASE_STORE_PASSWORD}" \
--ks-key-alias "${RELEASE_KEY_ALIAS}" \
--key-pass "pass:${RELEASE_KEY_PASSWORD}" \
--v1-signing-enabled true \
--v2-signing-enabled true \
--v3-signing-enabled false \
--v4-signing-enabled false \
"$APK"
echo "=== signature schemes after re-sign ==="
"$APKSIGNER" verify -v "$APK" | grep -i "Verified using" || true

- name: Rename artifact
run: cp android/app/build/outputs/apk/release/app-release.apk app-release-fdroid.apk

- name: Upload F-Droid APK artifact
uses: actions/upload-artifact@v7
with:
name: app-release-fdroid
path: app-release-fdroid.apk

release:
needs: [build, build-fdroid]
needs: [build]
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
permissions:
Expand All @@ -305,14 +125,8 @@ jobs:
with:
name: app-release

- uses: actions/download-artifact@v8
with:
name: app-release-fdroid

- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
app-release.apk
app-release-fdroid.apk
files: app-release.apk
generate_release_notes: true
90 changes: 0 additions & 90 deletions .github/workflows/sentry-noise-gate-report.yml

This file was deleted.

8 changes: 1 addition & 7 deletions .maestro/flows/activation-negative-401.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: cc.agentlabs.opencode
appId: it.kjeldahl.opencode
name: Activation - negative path (connect-time 401 must surface a visible error)
---
# Negative activation flow — regression test for the GitHub issue #76 failure
Expand Down Expand Up @@ -38,12 +38,6 @@ name: Activation - negative path (connect-time 401 must surface a visible error)

- launchApp:
clearState: true
- assertVisible:
id: "telemetry-consent-card"
- tapOn:
id: "telemetry-decline-button"
- takeScreenshot: negative-S1_consent_dismissed

- assertVisible:
text: "No Connection"
- tapOn:
Expand Down
14 changes: 4 additions & 10 deletions .maestro/flows/activation-positive.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
appId: cc.agentlabs.opencode
name: Activation - positive path (consent -> connect -> send message)
appId: it.kjeldahl.opencode
name: Activation - positive path (connect -> send message)
---
# Positive activation flow: first launch -> telemetry consent -> quick connect
# Positive activation flow: first launch -> quick connect
# to the mock opencode server (tests/fixtures/mock-opencode-server.ts, run
# normally on the port below) -> connected indicator -> new session -> send a
# message -> assert it sends (optimistic echo), WITHOUT waiting for the
Expand Down Expand Up @@ -34,7 +34,7 @@ name: Activation - positive path (consent -> connect -> send message)
# output in production; the bug they hit there was the 401-retry storm, not
# a missing/undelivered reply). Asserting on the reply here would therefore
# be asserting on a CI-harness limitation, not real app behavior — so this
# flow verifies everything reliably observable (consent, connect, session
# flow verifies everything reliably observable (connect, session
# creation, message send) and stops short of the SSE round trip. If a real
# fix for the underlying RN-Android streaming limitation ever lands (a native
# SSE module, WebSocket transport, etc.), restore the extendedWaitUntil +
Expand All @@ -43,12 +43,6 @@ name: Activation - positive path (consent -> connect -> send message)

- launchApp:
clearState: true
- takeScreenshot: positive-S1_launch_consent_modal
- assertVisible:
id: "telemetry-consent-card"
- tapOn:
id: "telemetry-decline-button"

- assertVisible:
text: "No Connection"
- takeScreenshot: positive-S2_empty_state
Expand Down
7 changes: 1 addition & 6 deletions .maestro/flows/all-sessions.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
appId: cc.agentlabs.opencode
appId: it.kjeldahl.opencode
name: All sessions across directories - list + open-across-project (regression #46/#48)
---
# New-feature regression coverage for the "all sessions across all projects"
Expand Down Expand Up @@ -31,11 +31,6 @@ name: All sessions across directories - list + open-across-project (regression #

- launchApp:
clearState: true
- assertVisible:
id: "telemetry-consent-card"
- tapOn:
id: "telemetry-decline-button"

- assertVisible:
text: "No Connection"
- tapOn:
Expand Down
Loading
Loading