Skip to content

[P1 validation/coverage] Kotlin workout backfill owner makes JaCoCo drop exact coverage #101

Description

@bhelm

Summary

The Android differential runner executes IntelligenceEngine.backfillWorkoutFromDetectedBout and produces 12/12 exact results against Swift, but JaCoCo cannot instrument the helper's owner class. On current upstream main (557f3b4d816fb6cf1f8ac55e2824d99732ca29d8), the unrelated IntelligenceEngine.analyzeRecentOnCpu has a legal 56,072-byte Code body that grows to 66,375 bytes when JaCoCo 0.8.12 inserts probes, exceeding the JVM's 65,535-byte method limit. JaCoCo rejects the entire IntelligenceEngine transform, so the executed 103-byte backfill helper reports zero coverage.

This is P1 validation/coverage, not a demonstrated Product runtime bug: direct Product tests and exact differential output are green, but the repository cannot provide the exact executed-method evidence required by #17/#26 and therefore cannot close the combined Native acceptance lane honestly.

Exact failure

A clean targeted parity run is green at the JUnit level (1/0/0, 1.258 s), but its XML contains:

IllegalClassFormatException: Error while instrumenting com/noop/analytics/IntelligenceEngine with JaCoCo 0.8.12
Caused by: MethodTooLargeException: Method too large: com/noop/analytics/IntelligenceEngine.analyzeRecentOnCpu (...)

The JVM continues with the original uninstrumented class, so Product behavior executes but no probes from that class can be recorded. The exact-method gate then fails closed:

ERROR: exact JaCoCo method com/noop/analytics/IntelligenceEngine::backfillWorkoutFromDetectedBout$app_fullDebug(Lcom/noop/data/WorkoutRow;IILjava/lang/Double;Ljava/lang/Double;)Lcom/noop/data/WorkoutRow; covered=0

Measured evidence

The preserved targeted capture at combined HEAD fd372a13053a20bed33687a416a41a2095cff0c8 is:

artifact exact result
IntelligenceEngine.class 388,856 bytes; JaCoCo class ID c405e3320326bae0; SHA-256 2cad0dc8f0b1114a77864aaa74ce3d6bf2f3a3aae4c6800f7b0c0513ddc5b2d6
analyzeRecentOnCpu 55,897-byte original Code; 66,113 bytes after probes
backfillWorkoutFromDetectedBout$app_fullDebug 103-byte Code; descriptor (Lcom/noop/data/WorkoutRow;IILjava/lang/Double;Ljava/lang/Double;)Lcom/noop/data/WorkoutRow;
JaCoCo exec 47,632 bytes; SHA-256 fbfe132d4d47a91fc89f546b15cadfc4701d8fb78da05cda8978215a6c55dbb0; session bernd-29d3d985; 651 execution classes; no IntelligenceEngine
JaCoCo XML 10,413,901 bytes; SHA-256 cc33f4c0218932c19f3b5a6d92e39d2ec9fd9d341aab2c9fcc9dd3e71bf2df69
XML backfill counters 57 instructions, 8 branches, 6 lines, 1 method; all missed/zero covered
XML analyzeRecentOnCpu counters 12,359 instructions, 416 branches, 570 lines, 1 method; all missed/zero covered
positive execution 12 Swift + 12 Kotlin receipts; 12 cases, 0 diffs; full corpus 1,288 cases, 0 diffs
direct Kotlin regression tests 3 tests, 0 failures/errors

Current upstream main independently reproduces the same boundary with a newly compiled IntelligenceEngine.class: 392,181 bytes, JaCoCo ID f3ffc4a938ba67c4, SHA-256 8635f530760908d9c70906d66fbcb8537505a3b36ba8bb803d1290c24401c429, original analyzeRecentOnCpu Code 56,072 bytes, and attempted instrumented size 66,375 bytes. Current WorkoutDetector.class is only 24,597 bytes (JaCoCo ID e62535763c7affa6).

The exact XML has no class-ID attribute; the IDs above are JaCoCo CRC64 identities from the matching compiled class receipt. The XML owner is com/noop/analytics/IntelligenceEngine, source IntelligenceEngine.kt, method backfillWorkoutFromDetectedBout$app_fullDebug, line 1795 in the captured build.

Cause and owner asymmetry

Upstream ryanbr#708 deliberately extracted the overlap merge into small pure helpers on both platforms. Swift owns its helper as WorkoutDetector.backfillWorkout; Kotlin owns its twin as IntelligenceEngine.backfillWorkoutFromDetectedBout. JaCoCo instruments at class granularity, so Kotlin's tiny helper inherits an unrelated class-wide instrumentation failure from the giant coroutine method. Swift has no corresponding owner coupling.

The relevant owner-sensitive references are the Kotlin Product overlap call and declaration, the sole Kotlin differential runner call, the three direct Kotlin tests, the twin-map/coverage identity, and the Swift twin comment/identity.

Acceptance — no waiver

  • Move the unchanged Kotlin pure helper to WorkoutDetector.backfillWorkoutFromDetectedBout, matching the Swift WorkoutDetector owner. Preserve the same inputs, output, visibility needed by Product/tests, and exact field semantics.
  • Change the sole Kotlin Product call, differential runner call, direct tests, Swift/Kotlin twin identity, and exact coverage owner to WorkoutDetector.
  • Remove the helper from IntelligenceEngine; no forwarding wrapper, duplicate implementation, test-only copy, exclusion, exemption, or coverage waiver may remain there.
  • Preserve Product semantics exactly: fill only missing avgHr/maxHr/energyKcal/strain; never overwrite present values; preserve every other row field; an already-complete row remains equal to its input.
  • Prove RED on the unchanged owner with the owner/admission guard and MethodTooLargeException, then GREEN from a fresh empty JaCoCo exec lifecycle with a matching loaded WorkoutDetector class ID and exact backfillWorkoutFromDetectedBout$app_fullDebug coverage greater than zero.
  • Every affected/full JUnit XML must contain no MethodTooLargeException, IllegalClassFormatException, or JaCoCo instrumentation error.
  • Keep the three direct Kotlin cases and all 12 exact Swift/Kotlin differential cases green, then rerun affected/full Android and the combined coverage ratchet without weakening any fail-closed check.
  • Do not refactor analyzeRecentOnCpu as a prerequisite; that is a separate scope from decoupling this helper's validation owner.

Provenance and duplicate audit

Open and closed issues and pull requests in both repositories were searched for JaCoCo, MethodTooLarge, “method too large”, IntelligenceEngine, coverage, and the exact helper. No exact duplicate was found.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions