Re-measure coverage after wave 2, and write down how a stacked PR merges #166

Merged
JMR-dev merged 1 commits from docs/coverage-wave2 into main 2026-08-29 16:33:20 +00:00
JMR-dev commented 2026-08-29 16:16:22 +00:00 (Migrated from github.com)

Two entries, both from work that just landed.

Coverage

87.1% line / 69.1% branch, 502 tests → 88.9% line (2087/2348), 75.4% branch (1011/1340), 546 tests in 76 classes. Measured on main at c6e9a48, after #161–#165.

The entry now explains the branch move rather than just quoting it, because only part of it is new tests:

before after change
branches covered 974 1011 +37
branches total 1410 1340 −70

Both are the seam work. Pulling a when out of a lambda inside a collect deletes the coroutine state machine's synthesized branches around it and leaves a plain function whose branches a test can choose. ConversionViewModel$observe$1$1 went from carrying the whole mapping to 6 branches, while the extracted ConversionViewModelKt covers 41 of 42 and JoinViewModelKt 38 of 39.

So a seam is worth more than the tests it enables — it also stops the measurement counting scaffolding. And a percentage that rises because the denominator shrank is a different claim from one that rises because more branches are tested. This entry has a documented history of explaining its own movements wrongly, so it now says which is which.

Stacked PRs

A new Conventions entry, from two traps measured on 2026-08-27 while landing #144–#151.

gh pr merge does not work on a stacked PR. It uses the GraphQL mutation, which refuses: "This pull request is part of a stack and must be merged using the asynchronous merge REST API." The plain PUT .../pulls/{n}/merge refuses too. What works:

gh api -X PUT repos/OWNER/REPO/pulls/N/merge-async -f merge_method=merge
# returns a uuid; poll .../merge-async/{uuid} until status is merged or failed

The second trap is worse, because nothing looks wrong. GitHub retargets a stacked PR's base to main when the PR below it merges — but asynchronously. Merging five about thirty seconds apart outran that, so each merged into its own base branch, which had itself already been merged and left behind. Every call returned status: merged. Every PR read MERGED. gh pr list --state open was empty. None of the content was on main.

What caught it was a coverage re-measure reading two points below what the same tree had produced an hour earlier. A fresh git pull changed nothing, which is what turned it from "stale checkout" into a real question. git merge-base --is-ancestor <merge-sha> origin/main answered it in one line, five times. #160 is what the recovery cost.

Also recorded: the auto-retarget belongs to the stacking feature specifically. A PR opened with a plain gh pr create --base some-branch does not retarget when that branch merges — it is left pointing at a dead base and has to be moved by hand, which is what #163 needed.

Docs-only; ktlintCheck and detekt clean.

Two entries, both from work that just landed. ## Coverage **87.1% line / 69.1% branch, 502 tests → 88.9% line (2087/2348), 75.4% branch (1011/1340), 546 tests in 76 classes.** Measured on `main` at `c6e9a48`, after #161–#165. The entry now explains the branch move rather than just quoting it, because **only part of it is new tests**: | | before | after | change | |---|---|---|---| | branches covered | 974 | 1011 | **+37** | | branches total | 1410 | 1340 | **−70** | Both are the seam work. Pulling a `when` out of a lambda inside a `collect` deletes the coroutine state machine's synthesized branches around it and leaves a plain function whose branches a test can choose. `ConversionViewModel$observe$1$1` went from carrying the whole mapping to **6** branches, while the extracted `ConversionViewModelKt` covers **41 of 42** and `JoinViewModelKt` **38 of 39**. So a seam is worth more than the tests it enables — it also stops the measurement counting scaffolding. And a percentage that rises because the denominator shrank is a different claim from one that rises because more branches are tested. This entry has a documented history of explaining its own movements wrongly, so it now says which is which. ## Stacked PRs A new Conventions entry, from two traps measured on 2026-08-27 while landing #144–#151. **`gh pr merge` does not work on a stacked PR.** It uses the GraphQL mutation, which refuses: *"This pull request is part of a stack and must be merged using the asynchronous merge REST API."* The plain `PUT .../pulls/{n}/merge` refuses too. What works: ```bash gh api -X PUT repos/OWNER/REPO/pulls/N/merge-async -f merge_method=merge # returns a uuid; poll .../merge-async/{uuid} until status is merged or failed ``` **The second trap is worse, because nothing looks wrong.** GitHub retargets a stacked PR's base to `main` when the PR below it merges — but asynchronously. Merging five about thirty seconds apart outran that, so each merged into its own base branch, which had itself already been merged and left behind. Every call returned `status: merged`. Every PR read `MERGED`. `gh pr list --state open` was empty. **None of the content was on `main`.** What caught it was a coverage re-measure reading two points below what the same tree had produced an hour earlier. A fresh `git pull` changed nothing, which is what turned it from "stale checkout" into a real question. `git merge-base --is-ancestor <merge-sha> origin/main` answered it in one line, five times. #160 is what the recovery cost. Also recorded: **the auto-retarget belongs to the stacking feature specifically.** A PR opened with a plain `gh pr create --base some-branch` does *not* retarget when that branch merges — it is left pointing at a dead base and has to be moved by hand, which is what #163 needed. Docs-only; `ktlintCheck` and `detekt` clean.
Sign in to join this conversation.