Derived from the canonical F102 CUE contract: **partial**.

Spec: F102 — Android Performance Release Gate

Status: Derived from the canonical F102 CUE contract: partial. See the generated traceability view and gap view; Markdown does not carry an editable completion claim.

1. Problem & CEO Job

Job: Trust the signal — but an Android release that regresses cold start time from 1.5s to 4s erases trust in the assistant’s reliability. The CEO’s $150 Android is the primary Lumi device; performance regressions that slip to production damage daily usage before they are noticed.

Without a performance gate, each Android release is a blind deploy:

  • Baseline profiles can drift stale without detection
  • Cold start time can regress without anyone noticing until users churn
  • No mechanism exists to distinguish “emulator validation” from “physical device truth”
  • The release manager has no checklist to verify performance before signing off
  • A stale or already-consumed versionCode can reach Gradle or Play
  • A Play API failure can be mistaken for an empty release history
  • An upload can be reported as published without proving the committed track state
  • A dev-backend AAB can be uploaded under the production package or to a non-internal track

2. User Stories & Acceptance Criteria

L1 — Code Gate (static verification)

  • As a reviewer, I want CI to verify the benchmark module and baseline artifact exist, so that a misconfigured build doesn’t silently skip benchmarks.
    • CI job fails if android/benchmarks/build.gradle.kts is missing
    • CI job fails if android/benchmarks/baselines/benchmark-baseline.json is missing
    • CI job fails if android/benchmarks/check_benchmark_regression.py is missing

L2 — Emulator Gate (deterministic benchmark)

  • As a release engineer, I want an emulator-based benchmark that measures cold start time deterministically, so that client-side regressions are caught before release without needing a physical device.
    • ColdStartBenchmark.startupColdNoCompilation — cold start, no AOT
    • ColdStartBenchmark.startupColdWithBaselineProfile — cold start with Baseline Profile
    • ColdStartBenchmark.startupWarm — warm start from recent tasks
    • PulseJourneyBenchmark.pulseTabNavigation — Pulse tab navigation
    • Benchmarks use debug signing, not release credentials
    • CI fails if any metric exceeds baseline + tolerance_pct

L3 — Physical Device Gate (human-operated)

  • As a release manager, I want a documented procedure for physical-device verification, so that emulator-vs-device differences are accounted for.
    • Physical device template exists at android/benchmarks/baselines/physical-device-template.json
    • Procedure documented in docs/performance/android-release-gate.md
    • ⚠ Not automated — requires a human with a reference phone

Sentry Crash Budget

  • As a release manager, I want to check the current release’s crash-free rate before promoting, so that a crashy build does not reach users.
    • Sentry crash-budget check documented but not automated — requires SENTRY_AUTH_TOKEN / SENTRY_DSN which are not available on CI runners
    • When credentials are missing, becomes a hard manual gate

Baseline Profile

  • As a release engineer, I want baseline profiles regenerated on the deploy/frontend-dev promotion path, so that new code paths are covered before the dev workspace moves forward.
    • BaselineProfileGenerator exists in :benchmarks module
    • The AndroidX Baseline Profile plugin is wired to :app and :benchmarks
    • android-baseline-profiles.yml invokes the canonical :app:generateProdReleaseBaselineProfile task on pixel6api31aosp
    • CI validates the generated variant profile and uploads it with managed-device diagnostics

Google Play Publication Integrity

  • As a release engineer, I want publication to fail closed against live Play state, so that a stale code or API outage cannot authorize an upload.
    • Production is bound to com.lumi.ai, production Firebase, and api.runlumi.app; development is bound to com.locuno.lumi.dev, Firebase project lumi-dev-5c0a0, and api-dev.runlumi.app
    • The dev publisher can commit only to the internal track and rejects every other track before Gradle
    • Both release scripts verify the built manifest package and BuildConfig.BASE_URL before uploading the exact new AAB
    • One uploaded edit adapts to deferred-review, automatic-review, and first-release draft requirements without re-uploading the consumed bundle
    • The checked-in versionCode must exceed the live Play maximum before Gradle
    • Failure to query every requested package blocks the release
    • The committed track is read through a fresh edit and must contain the exact code
    • CI runs focused mocked-session proof for preflight and readback behavior

3. Scope Boundaries

  • In:

    • Android Macrobenchmark module (:benchmarks)
    • Cold start, warm start, and Pulse journey benchmarks
    • Baseline Profile generator and deploy/frontend-dev promotion-path regeneration
    • CI gate for emulator-based regression detection
    • Checked-in baseline artifact with explicit emulator/device distinction
    • Release-verdict documentation
    • Regression checker Python script with unit tests
    • Static verification of benchmark module existence in CI
    • Fail-closed Play version-code preflight and exact post-commit track readback
    • Per-environment identity alignment across Gradle, Firebase, backend, track, and publishing
  • Out:

    • Physical device automated benchmarks (requires a device farm — not available)
    • Automated Sentry crash-budget check (credentials not available on runner)
    • iOS performance gate (separate concern)
    • Network latency benchmarks (not client-side)
    • GPU/rendering benchmarks (not part of this gate)
    • Companion web or backend performance regression gate

4. Layered Gate Diagram

flowchart TD
    L1["L1 Code Gate (CI)"]
    L2["L2 Emulator Gate (CI)"]
    L3["L3 Physical Device Gate (human)"]
    Sentry["Sentry crash-free check"]
    Verdict["Release verdict"]

    L1 -->|pass| L2
    L1 -->|fail| Block["Block — fix benchmark wiring"]
    L2 -->|pass| L3
    L2 -->|fail| Block2["Block — fix regression"]
    L3 -->|recorded| Sentry
    L3 -->|missing| ManualPass["⚠ Allowed if emulator gate passes
— physical gate documented as open"]
    Sentry -->|≥ 99.5%| Verdict
    Sentry -->|< 99.5%| Block3["Block — fix crashes"]
    Sentry -->|credentials missing| ManualGate["Manual sign-off required"]

5. Implementation

Files created

FilePurpose
android/benchmarks/build.gradle.ktsBenchmark module build config
android/benchmarks/src/main/AndroidManifest.xmlBenchmark module manifest
android/benchmarks/src/main/kotlin/.../ColdStartBenchmark.ktCold/warm start tests
android/benchmarks/src/main/kotlin/.../PulseJourneyBenchmark.ktPulse navigation test
android/benchmarks/src/main/kotlin/.../BaselineProfileGenerator.ktBaseline profile generator
android/benchmarks/baselines/benchmark-baseline.jsonChecked-in emulator baselines
android/benchmarks/baselines/physical-device-template.jsonPhysical device report template
android/benchmarks/check_benchmark_regression.pyRegression gate checker
android/benchmarks/tests/test_benchmark_regression.pyTests for regression checker
.github/workflows/android-benchmark-gate.ymlBenchmark CI workflow
docs/performance/android-release-gate.mdRelease-verdict documentation

Files modified

FileChange
android/settings.gradle.ktsAdded :benchmarks module
android/gradle/libs.versions.tomlAdded androidxBenchmark and benchmark libraries
android/app/build.gradle.ktsAdded benchmark build type and baselineProfile dependency
.github/workflows/android-baseline-profiles.ymlWired to :benchmarks module tasks
.github/workflows/android-ci.ymlAdded benchmark module verification step
deploy/DIAGRAMS.mdAdded benchmark gate to CI/CD pipeline diagram
docs/MVP_SCOPE.mdAdded F102 registry entry
docs/POLISH.mdUpdated F51 with benchmark gate note

6. Device Distinction

DeviceTypeGate layerTrust level
pixel6api31aospEmulator (GMD)L2 — CIRegression detection only
Pixel 7 (reference)PhysicalL3 — HumanRelease verdict target
User devices (any)Physical—Actual production performance

Emulator benchmarks use swiftshader_indirect GPU, no thermal throttling simulation, and no real network. They detect client-side code regressions, not absolute performance. Physical device values are expected to differ significantly.

7. Test Plan

TestLocationWhat it verifies
test_parse_benchmark_results_*android/benchmarks/tests/JSON parser handles both value_ms and median_ms formats
test_check_regression_*android/benchmarks/tests/Gate correctly passes/fails based on baseline + tolerance
verify_benchmark_module CI job.github/workflows/android-benchmark-gate.ymlBenchmark module and baseline files exist
AndroidPlayReleasePolicyTestsscripts/tests/test_android_play_release_policy.pyLive-version query failure blocks, preflight precedes Gradle, and track readback requires the exact code

8. Diagrams & Docs Touched

  • docs/performance/android-release-gate.md — new
  • deploy/DIAGRAMS.md — updated with benchmark gate
  • docs/MVP_SCOPE.md — added F102 entry
  • docs/POLISH.md — updated F51 performance columns

9. Implementation Audit

CheckStatus
Benchmark module compiles✅ Verified — :benchmarks:compileDevDebugKotlin BUILD SUCCESSFUL
Emulator benchmarks execute⚠ Not verified — requires self-hosted runner with KVM
Baseline profile generates⚠ Not verified — requires emulator run
Regression checker passes with sample data✅ Verified — 9/9 pytest passed
CI workflow wired correctly⚠ Not verified — needs GitHub Actions test

10. Bug / Change Log

2026-07-10 — Initial implementation

Type: Feature

Summary:

  • Created :benchmarks module with Macrobenchmark cold/warm start and Pulse journey tests
  • Created Baseline Profile generator with CI regeneration
  • Created CI benchmark gate workflow with regression checking
  • Created release-verdict documentation with explicit device distinction
  • Updated registry, CI diagrams, and POLISH

Notes:

  • Physical-device and Sentry crash-budget checks are documented but not automated — they require credentials or hardware not available on CI. Issue #1649 must remain open if this evidence is missing.
  • Git SHA: (to be filled at commit time)

2026-07-10 — r02: verify, polish, commit, rebase

Type: Polish + verification

Summary:

  • Verified benchmark module compiles (:benchmarks:compileDevDebugKotlin)
  • Verified Python regression checker (9/9 pytest)
  • Removed unused ManagedVirtualDevice import from app/build.gradle.kts
  • Updated implementation audit table
  • Committed and rebased onto origin/main

2026-07-14 — Contract Graph migration

Type: Contract ownership and docs reconciliation

Summary:

  • Added the canonical F102 CUE contract and structural proof markers for benchmark wiring, regression checking, and release-verdict evidence boundaries.
  • Corrected stale unchecked claims: the physical-device template, procedure, and Sentry check are documented; their external results remain open and blocking.

2026-07-20 — Fail-closed Play publication identity

Type: Release safety

Summary:

  • Added a live Play maximum check before Gradle for the production publisher; at that date the dev-backend release script was build-only. ADR-051 supersedes that boundary on 2026-08-02 with an isolated internal-only dev app.
  • Changed all-package query failure from a misleading zero result to a hard failure.
  • Added exact post-commit track readback and focused unit proof in Android CI.
  • Kept upload, edit commit, review, approval, and store availability as distinct states.

2026-08-02 — Isolated dev internal publisher

Type: Release safety and Android distribution identity

Summary:

  • Bound dev to package com.locuno.lumi.dev, Firebase project lumi-dev-5c0a0, api-dev.runlumi.app, and the Play internal track only.
  • Added pre-Gradle Firebase/package/version checks and post-build manifest/BuildConfig identity verification to both publishers.
  • Required signed dev releases and exact committed-track API readback; package, backend, and dev track are not operator-overridable.
  • Preserved production package com.lumi.ai and ADR-047’s replacement-app boundary.