Every PR — including test-only ones (new JVM tests under app/src/test/, docs, dev scripts) — triggers the full ~10-job E2E matrix (8 API levels + 2 preview shards, ~15 min each). With limited runners these queue, so merges serialize and trail the (fast) work. Observed acutely in the Robolectric UI-test epic (#373): 4+ test-only batch PRs queued behind E2E with nothing merging.
Goal
Skip the E2E jobs for PRs that don't touch app runtime (app/src/main/) or instrumented tests (app/src/androidTest/), so test-only/docs/script PRs merge on the fast gate (unit + static, ~8 min) instead of behind the E2E queue.
CRITICAL constraint (the real work)
The ci-passed gate needs: [… e2e, e2e-preview] and treats a skipped required job as a FAILURE (contains(needs.*.result, 'skipped') -> exit 1). A naive path-filter makes E2E skip -> the gate FAILS -> the merge is BLOCKED. The implementation MUST distinguish:
intentional path-filter skip -> gate passes
E2E failure/cancelled -> gate blocks
a changes/filter job that itself failed -> gate blocks (never wave a PR through on a broken filter)
Approach (design it robustly)
A changes job (e.g. dorny/paths-filter, pinned to a full SHA) computing whether app/androidTest/build code changed; make e2e + e2e-preview conditional on it; update the ci-passed gate so an intentionally-skipped E2E is OK — e.g. require changes=success AND each E2E result in {success, skipped} AND all other required jobs success.
CONSERVATIVE path set (err toward running E2E): treat app/src/main/**, app/src/androidTest/**, app/build.gradle.kts, gradle/**, settings.gradle*, root build files, and .github/workflows/ci.yml as needing E2E; treat changes limited to app/src/test/**, **/*.md, docs/**, scripts/**, .claude/** as E2E-skippable.
Note: the Robolectric batch PRs also edit app/build.gradle.kts (exclusion-list removals), so under the safe set they STILL run E2E — that is the correct conservative default. A content-aware exemption for jacoco-exclusion-only build.gradle edits is a possible stretch ONLY if provably safe; otherwise leave it out.
Acceptance
A docs/test-only PR skips E2E and STILL goes green on ci-passed; a PR with a failing E2E (or failing changes job) is still BLOCKED. Validate via the PR's own CI + explicit reasoning about the gate truth-table. Do not disturb #372 sharding / #388 diagnostics / #391 SDK-hardening.
## Problem
Every PR — including test-only ones (new JVM tests under `app/src/test/`, docs, dev scripts) — triggers the full ~10-job E2E matrix (8 API levels + 2 preview shards, ~15 min each). With limited runners these queue, so merges serialize and trail the (fast) work. Observed acutely in the Robolectric UI-test epic (#373): 4+ test-only batch PRs queued behind E2E with nothing merging.
## Goal
Skip the E2E jobs for PRs that don't touch app runtime (`app/src/main/`) or instrumented tests (`app/src/androidTest/`), so test-only/docs/script PRs merge on the fast gate (unit + static, ~8 min) instead of behind the E2E queue.
## CRITICAL constraint (the real work)
The `ci-passed` gate `needs: [… e2e, e2e-preview]` and treats a **`skipped`** required job as a FAILURE (`contains(needs.*.result, 'skipped')` -> exit 1). A naive path-filter makes E2E skip -> the gate FAILS -> the merge is BLOCKED. The implementation MUST distinguish:
- intentional path-filter skip -> gate **passes**
- E2E `failure`/`cancelled` -> gate **blocks**
- a `changes`/filter job that itself failed -> gate **blocks** (never wave a PR through on a broken filter)
## Approach (design it robustly)
A `changes` job (e.g. `dorny/paths-filter`, pinned to a full SHA) computing whether app/androidTest/build code changed; make `e2e` + `e2e-preview` conditional on it; update the `ci-passed` gate so an intentionally-skipped E2E is OK — e.g. require `changes`=success AND each E2E result in {success, skipped} AND all other required jobs success.
**CONSERVATIVE path set (err toward running E2E):** treat `app/src/main/**`, `app/src/androidTest/**`, `app/build.gradle.kts`, `gradle/**`, `settings.gradle*`, root build files, and `.github/workflows/ci.yml` as **needing E2E**; treat changes limited to `app/src/test/**`, `**/*.md`, `docs/**`, `scripts/**`, `.claude/**` as **E2E-skippable**.
Note: the Robolectric batch PRs also edit `app/build.gradle.kts` (exclusion-list removals), so under the safe set they STILL run E2E — that is the correct conservative default. A content-aware exemption for jacoco-exclusion-only build.gradle edits is a possible stretch ONLY if provably safe; otherwise leave it out.
## Acceptance
A docs/test-only PR skips E2E and STILL goes green on `ci-passed`; a PR with a failing E2E (or failing `changes` job) is still BLOCKED. Validate via the PR's own CI + explicit reasoning about the gate truth-table. Do not disturb #372 sharding / #388 diagnostics / #391 SDK-hardening.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Problem
Every PR — including test-only ones (new JVM tests under
app/src/test/, docs, dev scripts) — triggers the full ~10-job E2E matrix (8 API levels + 2 preview shards, ~15 min each). With limited runners these queue, so merges serialize and trail the (fast) work. Observed acutely in the Robolectric UI-test epic (#373): 4+ test-only batch PRs queued behind E2E with nothing merging.Goal
Skip the E2E jobs for PRs that don't touch app runtime (
app/src/main/) or instrumented tests (app/src/androidTest/), so test-only/docs/script PRs merge on the fast gate (unit + static, ~8 min) instead of behind the E2E queue.CRITICAL constraint (the real work)
The
ci-passedgateneeds: [… e2e, e2e-preview]and treats askippedrequired job as a FAILURE (contains(needs.*.result, 'skipped')-> exit 1). A naive path-filter makes E2E skip -> the gate FAILS -> the merge is BLOCKED. The implementation MUST distinguish:failure/cancelled-> gate blockschanges/filter job that itself failed -> gate blocks (never wave a PR through on a broken filter)Approach (design it robustly)
A
changesjob (e.g.dorny/paths-filter, pinned to a full SHA) computing whether app/androidTest/build code changed; makee2e+e2e-previewconditional on it; update theci-passedgate so an intentionally-skipped E2E is OK — e.g. requirechanges=success AND each E2E result in {success, skipped} AND all other required jobs success.CONSERVATIVE path set (err toward running E2E): treat
app/src/main/**,app/src/androidTest/**,app/build.gradle.kts,gradle/**,settings.gradle*, root build files, and.github/workflows/ci.ymlas needing E2E; treat changes limited toapp/src/test/**,**/*.md,docs/**,scripts/**,.claude/**as E2E-skippable.Note: the Robolectric batch PRs also edit
app/build.gradle.kts(exclusion-list removals), so under the safe set they STILL run E2E — that is the correct conservative default. A content-aware exemption for jacoco-exclusion-only build.gradle edits is a possible stretch ONLY if provably safe; otherwise leave it out.Acceptance
A docs/test-only PR skips E2E and STILL goes green on
ci-passed; a PR with a failing E2E (or failingchangesjob) is still BLOCKED. Validate via the PR's own CI + explicit reasoning about the gate truth-table. Do not disturb #372 sharding / #388 diagnostics / #391 SDK-hardening.