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) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user