From b0b8b66d3138a74ce524ce890fa07e4b8e1f848e Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Mon, 7 Sep 2026 16:19:24 -0500 Subject: [PATCH] Name reattach's third outcome, which this KDoc denied existed The determinism argument said no coroutine had a `_state` write left in flight, because `reattach` "has either returned on its `_state.value !is Idle` guard or found nothing". There is a third outcome: `pruneWork()` is async, so `reattach` can find an unpruned job, pass that guard, and start an `observe()` that is a live coroutine with writes ahead of it. The conclusion survives, by a mechanism the paragraph did not mention. `reattach` reads `ownership.current` before its query and hands that token to `observe`, while `onInputPicked` calls `ownership.claim()` synchronously on the pick -- so once a detail row exists that observation is superseded and every emission returns at `stillHeldBy` before it writes. The claim is therefore "every write in flight is landed or superseded", not "no other coroutine started". This is the KDoc a future reader opens to learn why the test cannot flake, and CLAUDE.md records the same failure mode twice already -- E1/E3, and #226's KDoc that described a draft rather than the code. A correct test with an incomplete explanation is its own defect. Comment only; no test logic changed. Committed with --no-verify on the repo owner's explicit say-so: the gate's cache is keyed on the `app/src` tree hash, so a comment costs a full 33-36 sweep, and CI is already green on the parent commit. ktlintCheck, detekt and compileDebugAndroidTestKotlin were run by hand first and pass -- those are what a comment edit can actually break. Co-Authored-By: Claude Opus 5 (1M context) --- .../saf/SafPickerRoundTripTest.kt | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/app/src/androidTest/java/org/libremediaconverter/saf/SafPickerRoundTripTest.kt b/app/src/androidTest/java/org/libremediaconverter/saf/SafPickerRoundTripTest.kt index d6db4fe..4a2e2fc 100644 --- a/app/src/androidTest/java/org/libremediaconverter/saf/SafPickerRoundTripTest.kt +++ b/app/src/androidTest/java/org/libremediaconverter/saf/SafPickerRoundTripTest.kt @@ -826,11 +826,22 @@ class SafPickerRoundTripTest { * * A detail row can only be composed from that second write, because `FileCard` renders the rows * exclusively under `input.probe != null`. So once one exists, both of `onInputPicked`'s writes - * have landed and been laid out, and no coroutine in the ViewModel has a `_state` write left in - * flight: `reattach` has either returned on its `_state.value !is Idle` guard or found nothing - * (teardown prunes finished work), and `observe` is not started until `convert()` runs. **The - * card cannot change height again before the tap**, which is a different claim from waiting - * longer. + * have landed and been laid out, and every `_state` write still in flight is either landed or + * superseded. + * + * `reattach` has **three** outcomes here, not two. It returns on its `_state.value !is Idle` + * guard; or it finds nothing; or — because `pruneWork()` is async and can leave a finished job + * unpruned — it passes that guard and starts an `observe()`. This paragraph used to name only + * the first two, which was wrong rather than merely incomplete: the third is a live coroutine + * with writes ahead of it. + * + * It is still harmless, and by a different mechanism than the guard. `reattach` reads + * `ownership.current` *before* its query and hands that token to `observe`, while + * `onInputPicked` calls `ownership.claim()` synchronously on the pick — so by the time a + * detail row exists the observation is superseded, and every emission returns at + * `stillHeldBy` before it writes. Outside that path `observe` is not started until + * `convert()` runs. **The card cannot change height again before the tap**, which is a + * different claim from waiting longer. * * The `Container` row specifically, rather than a new "probing finished" tag in `main`, because * this fixture is an MP4 video and that row is already what