Skip to content

fix(android): repositoryInsertV18Aux_insertsThenPrunes has been red on main since #888 - #897

Closed
vishk23 wants to merge 1 commit into
ryanbr:mainfrom
vishk23:fix/android-v18-aux-amortisation-parity
Closed

fix(android): repositoryInsertV18Aux_insertsThenPrunes has been red on main since #888#897
vishk23 wants to merge 1 commit into
ryanbr:mainfrom
vishk23:fix/android-v18-aux-amortisation-parity

Conversation

@vishk23

@vishk23 vishk23 commented Jul 27, 2026

Copy link
Copy Markdown

DeepCaptureMigrationTest.repositoryInsertV18Aux_insertsThenPrunes has been red on main since #888 landed.

Verified on a clean checkout of main at 2ff2af18 with nothing else applied:

com.noop.data.DeepCaptureMigrationTest > repositoryInsertV18Aux_insertsThenPrunes FAILED
    java.lang.AssertionError at DeepCaptureMigrationTest.kt:93
7 tests completed, 1 failed

I found it while running testFullDebugUnitTest as the local gate for an unrelated branch, and traced it back rather than assuming it was mine.

Why it broke, and why nothing caught it

#888 amortised the v18AuxSample retention sweep on both platforms. On the Swift side it also added the seam the tests need: WhoopStore.insert gained an internal v18AuxPruneEveryRows: parameter, and the existing retention tests pass 1 so they still prove newest-N-rows without banking 10,000 rows first. That is exactly what the PR body describes under TESTS.

The Kotlin twin got the behaviour but not the seam. WhoopRepository.insert reads V18_AUX_PRUNE_EVERY_ROWS (10,000) directly, so the existing test — which inserts one row and then asserts pruneV18Aux was called — now asserts a sweep that amortisation correctly defers.

It stays invisible because the Android workflow gates pushes and PRs against this repo's own branches; a Kotlin unit-test regression arriving on a merge does not fail anything. The four new amortisation tests #888 added are Swift-only, so the Kotlin path has had the behaviour with no coverage of it since.

The change

Mirrors the Swift seam rather than inventing a second one. WhoopRepository.insert takes v18AuxRetentionRows and v18AuxPruneEveryRows as defaulted parameters, so every production caller is unchanged and only tests pass anything.

The failing test now passes v18AuxPruneEveryRows = 1 for the same reason the Swift ones do, and proves what it always meant to: a batch that wrote rows is followed by the rolling prune, carrying the shipped retention constant.

Adds the four amortisation cases as Kotlin twins of the Swift ones:

  • deferred below the threshold — written, never swept
  • swept once the threshold is crossed, across batches, so what is pinned is the counter accumulating rather than one big batch tripping it
  • the counter resetting after each sweep, so a long offload sweeps repeatedly rather than once
  • the budget not shared between devices — this one fails against a shared counter, which is the invariant Amortize the v18 aux retention sweep instead of running it per insert batch #888's per-device map exists for

A shared Proxy-DAO recorder replaces the per-test one so the five cases read as variations on one setup instead of five copies of it.

No production behaviour changes: same constants, same sweep, same best-effort runCatching with the CancellationException rethrow.

Verification

  • ./gradlew testFullDebugUnitTest on this branch: 3193 tests, 0 failures, 5 skippedDeepCaptureMigrationTest: 11 passed
  • Same command on main immediately before: 1 failed

…ft twin got

#888 amortised the v18AuxSample retention sweep on both platforms, but only the Swift
side got the seam its tests need. `WhoopStore.insert` gained an internal
`v18AuxPruneEveryRows:` parameter and its retention tests pass 1, so they still prove
newest-N-rows without banking 10,000 rows first. `WhoopRepository.insert` read the
10,000 constant directly, and `DeepCaptureMigrationTest.repositoryInsertV18Aux_insertsThenPrunes`
was left asserting a sweep that amortisation now defers.

So that test has been RED on main since #888 landed. It is not visible in CI: the Android
workflow builds and tests on push/PR to this repo's own branches, and a Kotlin unit-test
regression on a merge does not gate anything. Verified on a clean checkout of upstream main
at 2ff2af1 with nothing else applied: `7 tests completed, 1 failed`.

Mirrors the Swift seam exactly rather than inventing a second one: `insert` takes
`v18AuxRetentionRows` and `v18AuxPruneEveryRows` as defaulted parameters, so every
production caller is unchanged and only the tests pass anything. The failing test now
passes `v18AuxPruneEveryRows = 1` for the same reason the Swift ones do, and proves what
it always meant to — that a written batch is followed by the rolling prune, carrying the
shipped retention constant.

Adds the four amortisation cases as Kotlin twins of the Swift ones, since the Kotlin path
had the behaviour but no coverage of it: deferred below the threshold, swept once crossed
(across batches, so the counter's accumulation is what is being pinned), the counter
resetting so a long offload sweeps repeatedly, and the budget not being shared between
devices — that last one fails against a shared counter, which is the invariant #888's
per-device map exists for.

The shared Proxy-DAO recorder replaces the per-test one so the five cases read as variations
on one setup rather than five copies of it.

Verification: ./gradlew testFullDebugUnitTest — 3193 tests, 0 failures, 5 skipped
(DeepCaptureMigrationTest: 11 passed). Same command on main before this change: 1 failed.
@vishk23

vishk23 commented Jul 27, 2026

Copy link
Copy Markdown
Author

Superseded by #904, which landed the same fix upstream — the injectable prune-threshold seam is now on main, so this is redundant. Closing rather than rebasing.

@vishk23 vishk23 closed this Jul 27, 2026
ryanbr added a commit that referenced this pull request Jul 28, 2026
…ct (#914)

The #103 probe's no-answer verdict printed one sentence — "neither
GET_FF_VALUE(128) nor GET_DEVICE_CONFIG_VALUE(121) is served by this firmware"
— for three runs that establish three different things, and for one that
establishes the opposite.

Two timeouts got it. But `BLEManager.send` can return without transmitting at
all: there is an early return when `cmdCharacteristic` is missing, and another
in the 5/MG allowlist else-branch. So a run in which nothing ever reached the
strap rendered a claim about what the strap's firmware serves. The file's own
header names answered / rejected as UNSUPPORTED / silent as three distinct
outcomes and then the verdict collapsed silence into the same factual claim as
a refusal.

One refusal got it for both verbs. The `||` meant 128 answering UNSUPPORTED(3)
while 121 timed out printed "neither ... is served — rejected as UNSUPPORTED".
121 was never refused; it was never heard from.

An undecodable reply got the bare "is not served" sentence. A CRC or envelope
failure is affirmative evidence the strap DID transmit, which is the opposite
of what that sentence says.

The sentence is now per-verb and tied to the evidence that produced it:
UNSUPPORTED is "refused by firmware (UNSUPPORTED)", a timeout is "served no
reply in Ns — unconfirmed" (the window is now recorded, not just traced),
an undecodable reply is "replied but the frame did not decode — unconfirmed",
and an untried verb is "not asked". Only UNSUPPORTED is the firmware
answering, so the strong "not served by this firmware" wording survives in
exactly one case: the firmware refused BOTH verbs itself, where the `||` is
now an `&&`.

Same defect class as the ECG probe fix on #896: a verdict asserting a
conclusion the run's own inputs could not support. Mirrored byte-for-byte in
com.noop.protocol.DeviceConfigReadProbe; the golden cross-platform report test
is unaffected because it exercises the answered path.

swift test 428 passed, 0 failures; gradlew testFullDebugUnitTest 3199 tests,
0 failures, 5 skipped, verified from the JUnit XML (the
DeepCaptureMigrationTest failure noted in #897 is fixed on current main and
passes here).

Refs #103.

Co-authored-by: Fanboynz <mp3geek@gmail.com>
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