Skip to content

chore: sync master to dev - #220

Merged
stslex merged 6 commits into
devfrom
chore/sync-master-to-dev
Aug 11, 2026
Merged

chore: sync master to dev#220
stslex merged 6 commits into
devfrom
chore/sync-master-to-dev

Conversation

@stslex

@stslex stslex commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Auto-generated PR to propagate master changes (version bumps, hotfixes) back to dev.

github-actions Bot and others added 6 commits August 10, 2026 18:53
`killGeneration` cancelled the generation's scope and opened the next one on the
very next line. DataStore drops a file from its process-global `activeFiles` set
inside an `invokeOnCompletion` handler on that scope's Job (`SimpleActor.init` ->
`DataStoreImpl.onComplete` -> `StorageConnection.close`), which runs when the job
*completes*, not when `cancel` returns — and a write-actor child still detaching
on Dispatchers.IO holds the job in COMPLETING_WAITING_CHILDREN. So the reopen
raced the removal and tripped `check(!activeFiles.contains(path))`,
FileStorage.kt:52, "There are multiple DataStores active for the same file".

A quiet machine wins that race every time; a 4-worker CI runner does not. It went
red once on release/release-v.1.49.0 (run 31421333548, attempt 1) and green on the
re-run, having passed three times on PR #218 — the signature of a race, not a break.

`cancelAndJoin` makes the handover deterministic. Proven rather than assumed:
holding the job in Cancelling for 600ms reproduces the exact exception with the
bare `cancel` and survives it with the join.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYLZo1s6f53WJ21Psk69tu
AGP wires a module's own test fixtures onto its androidTest runtime classpath. This module
is the only one that both enables testFixtures and ships an androidTest source set, so
`testFixturesImplementation(libs.paparazzi.core)` put Paparazzi's JVM rendering stack —
layoutlib, com.android.tools:sdk-common, protobuf-java — into an instrumented-test APK,
where it collided with the protobuf-javalite that :core:core brings via firebase-perf.

That reddened :core:ui:kit:checkDebugAndroidTestDuplicateClasses and, behind it,
mergeDebugAndroidTestJavaResource on google/protobuf/empty.proto and then on JUnit 5's
META-INF/LICENSE.md. It blocked the v1.49.0 production deploy (run 31429081872), and it had
been latent since 4af7918 (2026-07-26) because nothing compiles androidTest before the
deploy job.

The Paparazzi plugin was never the leak: eight other modules apply it alongside androidTest
sources and stayed clean. dependencyInsight on debugAndroidTestRuntimeClasspath listed
app.cash.paparazzi before this change and finds nothing after.

compileOnly loses nothing at runtime. The harness only ever executes on a testDebugUnitTest
classpath, and all twelve modules consuming testFixtures(project(":core:ui:kit")) apply the
Paparazzi plugin themselves and get junit-jupiter from the convention plugin's test bundle —
set containment checked, not assumed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NvAARa5m1bXMKxuVYeo86t
… contradict

chart_singlePoint_rendersCanvasWithoutCrash asserted that a one-point state renders
ChartCanvas. Commit 9139d8c (2026-07-29) made that false in the same diff that wrote it:
State.content resolves `points.size >= MIN_CHART_POINTS` to Content.Plot and anything below
it to Content.Loading, so the screen composes a loading indicator and the canvas is
unreachable. The test has not passed since, and only surfaced now because the smoke suite
runs once per release.

The canvas keeps its own below-two-points fallback — gridlines, then return — so what was
lost is reachability, not the maths. Reachability is the more useful thing to pin: it is
ChartContent's own claim that "there is no arrangement of fields here that can put an empty
chart on screen", and CommonHandler resolves every sub-threshold dataset to
EmptyReason.NO_DATA_FOR_EXERCISE, so the state this test builds cannot occur in production
and hand-construction is the only way to attack the gate.

Renamed to say what it now checks. Measured, not assumed: relaxing the branch to
`points.isNotEmpty()` turns it red and names the test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NvAARa5m1bXMKxuVYeo86t
assembleDebug does not build the androidTest variants, and the only workflow that does —
ui_tests.yml — is workflow_dispatch/workflow_call only. So the instrumented source sets were
compiled exactly once per release, inside android_deploy_prod.yml's ui_tests job, downstream
of a guard and a build that had both already gone green.

That is how a Paparazzi test-fixture leak from 2026-07-26 and a chart test broken on
2026-07-29 both reached the v1.49.0 deploy (run 31429081872) unseen: the last standalone
ui_tests run was 2026-05-03.

assembleDebugAndroidTest reproduces the build-graph half with no emulator. Measured, not
assumed: reintroducing the fixture leak fails this step at
:core:ui:kit:checkDebugAndroidTestDuplicateClasses, the exact task the deploy died on.

It assembles the test APKs, it does not run them. Execution still needs a device and stays
in ui_tests.yml — the chart test in that same run went red on an assertion, which is out of
this step's reach by construction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NvAARa5m1bXMKxuVYeo86t
- Mode: `release`
- Source branch: `dev`
- versionName: `1.49.0`
- versionCode: `50`

<!-- changelog generated at deploy time -->

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d6d6b6ac6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# and stays in `ui_tests.yml` — a test that goes red on an assertion (as the chart test in
# that same run did) is out of this step's reach by construction.
- name: Assemble instrumented tests
run: ./gradlew assembleDebugAndroidTest --full-stacktrace

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Force the instrumented-test gate to execute

When matching Android-test outputs exist in the Gradle build cache restored earlier in this workflow, this invocation can succeed with tasks reported FROM-CACHE, so the new gate does not actually compile the instrumented source sets on every PR as claimed. Gradle 9.5 --help describes --no-build-cache as “Disables the Gradle build cache”; use the repository's documented --rerun-tasks --no-build-cache --continue phase-exit command so this gate supplies fresh evidence.

AGENTS.md reference: AGENTS.md:L43-L49

Useful? React with 👍 / 👎.

Comment on lines +107 to +111
# Measured, not assumed: the v1.49.0 deploy (run 31429081872) died on two of them —
# `:core:ui:kit:checkDebugAndroidTestDuplicateClasses` and
# `mergeDebugAndroidTestJavaResource`, from a Paparazzi test-fixture leak that landed
# 2026-07-26 and sat unseen for a month because the last standalone `ui_tests` run was
# 2026-05-03. Both are build-graph failures: this step alone reproduces them, and it needs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move the release incident history out of the workflow

This run number, failure chronology, and dated account of what previously happened are release history rather than a guard needed where the command is edited. Keep only the current assemble-versus-execute constraint here and move the incident record to the commit body or the relevant documentation registry.

AGENTS.md reference: AGENTS.md:L103-L108

Useful? React with 👍 / 👎.

Comment on lines +74 to +78
* the job *completes*, not when `cancel` returns. Its `Dispatchers.IO` children keep the job in
* Cancelling for as long as they take to unwind, so opening the next generation without joining
* races that removal and trips `check(!activeFiles.contains(path))` — "There are multiple
* DataStores active for the same file" (FileStorage.kt:52). That race is what made this test flake
* red on a loaded CI runner while staying green locally.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Replace the unstable source-line citation

The FileStorage.kt:52 reference is an explicitly prohibited line-based citation, and a repo-wide search for the distinctive activeFiles and exception text finds no documentation containing this derivation. Add the derivation to documentation first, cite it by a stable anchor, and retain only the cancelAndJoin guard and conclusion in this KDoc.

AGENTS.md reference: AGENTS.md:L115-L126

Useful? React with 👍 / 👎.

Comment on lines +55 to +59
// `checkDebugAndroidTestDuplicateClasses` and `mergeDebugAndroidTestJavaResource` both failed
// (the latter on `google/protobuf/empty.proto`, then on JUnit 5's `META-INF/LICENSE.md`).
// Measured, not assumed: `:core:ui:kit:dependencyInsight --configuration
// debugAndroidTestRuntimeClasspath --dependency app.cash.paparazzi` listed it before this
// change and finds nothing after; the eight other modules that apply the Paparazzi *plugin*

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Relocate the dependency-incident derivation

This block records prior failing tasks, collision artifacts, dependency-insight measurements, and what used to leak, duplicating incident history and derivation at the dependency declaration. Preserve only the guard that compileOnly keeps the JVM harness off androidTest and the consumer precondition here; first record the supporting analysis in documentation, then cite its stable anchor.

AGENTS.md reference: AGENTS.md:L103-L108

Useful? React with 👍 / 👎.

Comment on lines +162 to +166
// v3 put a SECOND, earlier gate in front of the canvas. The canvas still keeps its own
// below-two-points fallback — it draws gridlines and returns, index spacing needs n ≥ 2
// (ChartCanvas.kt) — but the screen no longer routes a sub-threshold dataset there at
// all: `points.size >= MIN_CHART_POINTS -> Content.Plot`, else `Content.Loading`
// (ExerciseChartStore.kt). So what this pins is reachability, not the canvas's maths.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove redesign history from the UI test

The account of what “v3 put” in front of the canvas and the mutation experiment are history and derivation rather than a point-of-edit guard; the renamed test and its assertions already express the current reachability contract. Record the derivation in documentation first and leave only a concise conclusion with an anchor citation if the invariant still needs explanation.

AGENTS.md reference: AGENTS.md:L103-L108

Useful? React with 👍 / 👎.

@github-actions

Copy link
Copy Markdown
Contributor

Unit Test Results

1 999 tests   1 999 ✅  11m 18s ⏱️
  226 suites      0 💤
  226 files        0 ❌

Results for commit 0d6d6b6.

@stslex
stslex merged commit 216dfce into dev Aug 11, 2026
15 checks passed
@stslex
stslex deleted the chore/sync-master-to-dev branch August 11, 2026 18:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant