ADFA-4128 (11/11): app + bench — wiring Quick Build into the IDE and the benchmark harness - #1723
Open
fryanpan wants to merge 2 commits into
Open
Conversation
fryanpan
force-pushed
the
feature/ADFA-4128-qb-11-app
branch
from
August 22, 2026 06:41
5b48f90 to
c69d8ef
Compare
…adds the debug-only benchmark harness The app wiring and the bench surface land together because they are mutually dependent: :app's ProjectHandlerActivity and QuickBuildModule call into QuickBuildBenchHooks, and QuickBuildBenchHooks returns AutostartBuild and resolves EnvironmentQuickBuildPaths. Neither ordering of a two-PR split compiles. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kj9YeCDHGp9DU8LPtfWJ7W
…ent, test gaps Important 1 (daemon idle-timeout/Metaspace tuner un-gated): kept un-gated by design — the 384m Metaspace floor fixes real OOM-killed builds and the tiered idle timeouts keep low-RAM devices from losing the IDE to lmkd; GradleBuildTuner now states this in its KDoc. Ships flag-off; needs Bryan sign-off in the PR body. Important 2 (generateSources narrowing un-gated): judged a genuine all-users improvement, not QB-specific — the old code ran a Gradle generateSources after EVERY save-all (and after any XML save in SaveFileAction), a per-save build tax; flag-off the deferral degenerates to the same immediate call, so the narrowing is the only behavior change. Known trade (manifest-only edits leave generated Manifest/R intermediates stale until the next resource save or build) now stated at both call sites. Ships flag-off; needs sign-off in the PR body. Important 3 (install dropped on rotation, flag on): installApk's async path now re-arms AwaitingInstall (BuildViewModel.reArmInstall, fires only from Idle) from the coroutine's drop path, so a configuration change during the APK-manifest parse makes the recreated activity's collector retry the install instead of silently losing a successful build. Covered by BuildViewModelInstallReArmTest. Test gap (zip-slip guard): extraction loop extracted to QuickBuildArtifactStager.extractDaemonZip(InputStream, File); the guard is watched going red by QuickBuildArtifactStagerTest (a ../ entry throws and nothing lands outside the daemon dir). Test gap (InstallationEventFlow mapping): InstallationEventFlowTest pins the PackageInstaller status mapping, including the ABORTED-vs-FAILURE branch order and the no-extras / no-status paths. Test gap (service-side output capture): suppress/capture/drain routing extracted from GradleBuildService.logOutput into InternalBuildOutputCapture; bounded tail, drain-clears, throwing progress listener and editor-listener routing pinned by InternalBuildOutputCaptureTest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Kj9YeCDHGp9DU8LPtfWJ7W
fryanpan
force-pushed
the
feature/ADFA-4128-qb-11-app
branch
from
August 22, 2026 07:06
c69d8ef to
5a3d5eb
Compare
fryanpan
marked this pull request as ready for review
August 23, 2026 02:31
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.
Tip: disable this comment in your organization's Code Review settings.
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.
Part 11/11 of the stacked split of #1669 (requested by Akash). Base: feature/ADFA-4128-qb-10-gradle-plugin. Stack overview + review mechanics: PR 1 (#1713). Terms are defined in quickbuild/README.md (lands in PR 1).
Puts Quick Build in front of the user: a button next to Run, and enough narration to tell what it is doing and when it has finished. It also adds a harness to make it easier to run standard Gradle build and Quick Build benchmarks, and to gather key metrics about the stages of the build process.
flowchart TB subgraph appc["<b>This PR: inside app/ — wiring and bench</b>"] act["QuickBuildAction<br/>registered only when<br/>FeatureFlags.isExperimentsEnabled<br/><i>QuickBuildAction.kt</i>"] --> mgr["QuickBuildManager<br/>session lifecycle, provisioning,<br/>stop-tap cancellation"] mgr --> narr["QuickBuildOutputNarrator<br/>attached to the session manager;<br/>queues while no pane is bound<br/><i>QuickBuildOutputNarrator.kt</i>"] mgr --> sb["status bar collector<br/>lifecycle-scoped: state, not history<br/><i>QuickBuildStatusBar.kt</i>"] koin["QuickBuildModule (Koin)<br/>binds every core port;<br/>assetsLiveReloadable read once<br/>at the Android edge<br/><i>QuickBuildModule.kt</i>"] tr["bench trampoline activity<br/>debug-source-set manifest only<br/><i>QuickBuildBenchActivity.kt</i>"] --> mgr mgr --> hooks["QuickBuildBenchHooks<br/>inert release twin<br/><i>debug/QuickBuildBenchHooks.kt</i>"] hooks --> rec["event + metrics recorders"] rec --> log["bench-events.jsonl<br/><i>BenchEventsFile.kt</i>"] hooks --> e2e["MODE_STANDARD_E2E<br/>measures the standard build<br/>through install + launch<br/><i>QuickBuildBenchAutostart.kt</i>"] end adb["adb shell am start<br/>gated on android.permission.DUMP"] --> tr mgr --> core[":quickbuild:core session manager (PRs 5-8)"] narr --> pane["Build Output pane (existing)"] sb --> bar["bottom status bar (existing)"] mgr -- "provisioning + rebuild builds" --> gbs["GradleBuildService (existing)"] classDef thisPrBox fill:#dbeafe,stroke:#93c5fd,color:#1e3a5f classDef inPr fill:#ffffff,stroke:#64748b,color:#000 class appc thisPrBox class act,mgr,narr,sb,koin,tr,hooks,rec,log,e2e inPrWhat to review
QuickBuildAction.kt— owns the session tap: start, stop-tap cancel, grey-out. Line-by-line.Gradle tuning — Metaspace 192→384 MB + daemon idle timeouts (30 min balanced / 2 h high-perf); the only changes to non-QB behavior
QuickBuildOutputNarrator.kt,QuickBuildStatusBar.kt— queued narration; lifecycle-scoped status showing state, not history.QuickBuildModule.kt— binds every core port; reads assetsLiveReloadable at the Android edge.GenerateSourcesDeferral.kt— defers resource-XML generateSources until Quick Build goes idle.John's items C15, C16, C17, C22, C23 folded in as fixes.
Rollback: without the flag there is no UI entry point.
Followup, not fixed: R8 emits kotlin.Metadata warning noise.
QuickBuildBenchAutostart.kt— MODE_STANDARD_E2E measures the standard build through install and launch. Line-by-line.The e2e latch bypasses install confirmations only for the measured span.
QuickBuildBenchActivity.kt,QuickBuildBenchHooks.kt— DUMP-gated trampoline; inert release twin.BenchEventsFile.kt— a failed relaunch omits relaunchOk rather than recording zero.How this PR Was Tested
Automated tests (see coverage details below)
Manual QA — walked the
manual-qa.mdtest plan on the A56 [measured on a56]Benchmark — full two-device pass (2026-08-22; 35-app corpus, 160 scripted edits; both arms on real devices): a warm code edit reaches the running app 5.4x faster than a standard build + deploy on a Samsung A56, and 7.3x faster on a low-spec A06 — the weaker the phone, the bigger the win. Medians over 87 / 82 paired edits, with the standard arm's install-dialog wait already subtracted, so the numbers are conservative. MODE_STANDARD_E2E drove the standard arm through install + launch on both devices during this pass.
Still open — the rebaseline relaunch metric (its column never populated; a dedicated bench is deferred), and the API 28/29 resource-swap success path.
Coverage (JaCoCo at the stack tip, single run):
A lot of this was UI code and wasn't covered very well by automated tests.
actions/buildactions/fileactivities/editoranalytics/quickbuildappApplicationclasses, Android-bounddifragments/sidebarhandlersquickbuildservices/builderService, Android-boundutilsviewmodelReview fixes (2026-08-22)
A review-fixes commit addresses the code-review findings. Two changes here deliberately ship to all users, with the Experiments flag off (approved):
One candidate followup from review (orchestrator forcing a full-changed compile after a failed dex/deploy) was re-checked and refuted at this tip: the forced flag re-arms and a forced no-op already performs the full rebuild. The daemon-side recovery lever stays in as defense in depth.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Kj9YeCDHGp9DU8LPtfWJ7W