Task 13.5: match a release bundle's debug id to the composed map - #43
Conversation
Gradle 9 had removed Project.exec, so the inject hook never reached that map. The dashboard symbolication half stays open. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Stale comment
Deep review — Task 13.5 local half (Gradle 9 inject + composed debug id)
Reviewed 5cd1209 against surrounding 13.1/13.4 code (
hermes-sourcemaps.js,ensureHermesHooks,debugIds.ts,bugsee-xcode.sh, existing exception e2e). The production change is the Gradle 9Project.execreplacement sohermes-sourcemaps.js finishactually runs after compose. The newexc-map-idpath correctly leaves the inject stub alone. The dashboard /debug-files uploadhalf is still open, and the plan checkbox stays unchecked — that matches the evidence.I did not run unit tests or a device e2e in this environment.
Findings
P2 — Expo plugin will not migrate an existing
project.exechook
- Location:
packages/react-native/plugin/src/gradle.ts(ensureHermesHooks, used with the new ExecOperations line)- Problem:
ensureHermesHooksreturns the file unchanged oncePRESERVE_REFUSALis present. A 13.4 prebuild already contains that marker andproject.exec. Re-running the config plugin (includingexpo prebuildwithout--clean) never rewrites the exec call.ensureAppAppliesPlugin(next) === nextinplugin.test.tslocks that write-once behavior in.- Impact: The Gradle 9 inject fix is what this PR exists to ship for customers. The bare example was patched by hand; Expo apps that already have the 13.4 hook keep the removed API.
createBundle*JsAndAssetsthen throwsMissingMethodExceptiononproject.exec, so the composed map is never injected and a release JS stack cannot join the uploaded map.- Scenario: Prebuild with 13.4 on RN 0.87 / Gradle 9.4 (this example's wrapper). Upgrade
@bugsee/react-nativeto this commit. Runexpo prebuildwithout--clean.android/app/build.gradlestill hasproject.exec.assembleReleasehits the finishdoLast(preserved.isFile()is true) and fails beforehermes-sourcemaps.js finish.- Fix: When the marker is already present, rewrite only the Bugsee finish-hook
project.exec {(the 20-space line inside thatdoLast) to the ExecOperations call. Do not globally replaceproject.execin the whole file —bundleTaskis not in scope elsewhere. Add a test that starts from a 13.4 hook and asserts the exec line changed. Prefer capturingExecOperationsatconfigureEachtime (objects.newInstance/ injected interface) rather thanbundleTask.services.getinsidedoLast; that is the public Gradle 9 replacement.P2 — iOS device suite is enabled but the composed map path is Android-only
- Location:
examples/bare/e2e/composed-debug-id.test.ts(composedMapPath)- Problem:
describeReleaseProofruns on iOS device, but the default map isandroid/app/build/generated/sourcemaps/react/release/index.android.bundle.map. iOS writes$CONFIGURATION_TEMP_DIR/main.jsbundle.composed.map(bugsee-xcode.sh).BUGSEE_COMPOSED_MAPexists, but nothing requires it on iOS.- Impact: The iOS half of this proof cannot succeed as written. You either throw on a missing Android map, or compare the iPhone runtime UUID to a leftover Android UUIDv5 (different bundle content → different id) and fail for the wrong reason.
- Scenario:
E2E_RELEASE=1 E2E_PLATFORM=ios E2E_IOS_TARGET=deviceon the XS, as the describe gate allows.beforeAllreads the Android path first, before any launch.- Fix: Skip iOS unless
BUGSEE_COMPOSED_MAPis set, or persist the iOS composed map to a stable checkout path during the Xcode hook and default to that whenON_IOS.Overall risk: Medium
The example's ExecOperations lookup did what 13.5 needed on WOD_LX1 (
debug_ids{"index.android.bundle":"3c110b23-2416-5bc6-acac-e35ac7e69aa6"}matching the composed map). The customer-facing Expo hook does not apply that same fix to trees that already have the 13.4 snippet. iOS is opted in without a map it can read.Merge recommendation
Request changes. Do not merge until the plugin rewrites an existing
project.execfinish hook (with a test). The iOS default path should not run against the Android map.Most important issues to fix
- Migrate the already-written Hermes finish hook off
project.execinensureHermesHooks.- Stop the iOS device describe from using the Android composed map unless an override is provided.
What looks good
- Replacing
Project.execis the correct Gradle 9 fix; all finish arguments are already absolute, so working-directory differences vsProject.execdo not matter here.exc-map-idskipsregisterDebugIds, which would have replaced the inject stub and hidden the real UUID. Launch still goes throughstartRun(URI on Android, launch args on iOS), so a release binary can select the scenario even thoughe2e-scenario.jsonis baked in.expect(run.dev).toBe(false), airplane mode, nonce-scoped reason, anddebug_id/debugIdequality on the map are the right local-half checks.- The plan note is honest that upload and dashboard symbolication were not done.
Sent by Cursor Automation: Bugsee code review
A 13.4 file that already had the preserve marker stayed on the API Gradle 9 removed. An iOS device proof now waits for BUGSEE_COMPOSED_MAP instead of reading the Android map. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Deep review — Task 13.5 local half (181a692)
Re-reviewed 181a692 against surrounding 13.1/13.4 code (hermes-sourcemaps.js finishAfterCompose, ensureHermesHooks, debugIds.ts / payload.ts, bugsee-xcode.sh, exceptions.test.ts payloadOf, startRun URI/launch-args). Both findings from 5cd1209 are fixed. The dashboard / debug-files upload half is still open by design; the plan checkbox stays unchecked.
This environment has no node_modules, so plugin unit tests were not executed here. At posting time GitHub CI on this HEAD was still running (lint, typecheck, unit, mutation pending).
Prior findings
P2 Expo project.exec migration — fully addressed. migrateFinishHookExec scopes the replace to the Bugsee bundleTask.doLast that contains "finish" after PRESERVE_REFUSAL, so a 13.4 hook is rewritten and a sibling project.exec is left alone. The new fixture starts from that stale line and asserts a second apply is a no-op. The lookup stays bundleTask.services.get(ExecOperations); that is the call the WOD_LX1 Gradle 9.4.1 release actually ran.
P2 iOS device vs Android map — fully addressed. describeReleaseProof now requires BUGSEE_COMPOSED_MAP on iOS device. Without it the suite is skipped and does not open index.android.bundle.map.
Findings
No new P0–P3. I looked for silent migration misses (brace matching vs the generated snippet, "finish" region, CRLF), ExecOperations cwd (all finish argv paths are already absolute), exc-map-id replacing the inject stub, awaitBundles / payloadOf vs the existing exception e2e, iOS afterAll ( clearBundles → clearIosBundles → terminateIosApp), and release steering (startRun still uses the Android URI / iOS launch args). None of those produced a credible failure on this HEAD.
Overall risk: Low
The production change is the Gradle 9 Project.exec replacement plus an upgrade rewrite for trees that already have the 13.4 hook. Android local half was device-proven on WOD_LX1 (debug_ids {"index.android.bundle":"3c110b23-2416-5bc6-acac-e35ac7e69aa6"} matching the composed map). iOS remains opt-in via BUGSEE_COMPOSED_MAP; that is documented, not a false failure.
Merge recommendation
Safe to merge once CI on 181a692 is green. Do not treat the 13.5 checkbox as done: upload and dashboard symbolication are still open.
Most important issues to fix
None on this HEAD.
What looks good
- Replacing
Project.execis the correct Gradle 9 fix; finish arguments are already absolute, so working-directory differences vsProject.execdo not matter here. - The migration is deliberately narrow (
^([ \t]*)project\.exec \{$inside thatdoLastonly). exc-map-idskipsregisterDebugIds, which would have replaced the inject stub and hidden the real UUID. Launch still goes throughstartRun(URI on Android, launch args on iOS), so a release binary can select the scenario even thoughe2e-scenario.jsonis baked in.expect(run.dev).toBe(false), airplane mode, nonce-scoped reason, anddebug_id/debugIdequality on the map are the right local-half checks.- The plan note is honest that upload and dashboard symbolication were not done.
Sent by Cursor Automation: Bugsee code review


Summary
3c110b23-2416-5bc6-acac-e35ac7e69aa6forindex.android.bundle.debug-files uploadis still not executed. The Task 13.5 checkbox stays open because the dashboard has not symbolicated a frame. No Bugsee app was created. The example credentials are the CI placeholder.Test plan
AMRJCP4718402860, release,E2E_RELEASE=1: runtimedebug_idsequals the composed map'sdebug_idlogExceptionout there, so a Release simulator build does not retaindebug_idsMade with Cursor