Pin the launcher layer, where two callbacks share a signature #214

Merged
JMR-dev merged 2 commits from test/launcher-callback-identity into main 2026-09-06 01:18:36 +00:00
JMR-dev commented 2026-09-02 23:46:52 +00:00 (Migrated from github.com)

Closes #200.

The hazard

ConversionViewModel.onInputPicked(uri: Uri) and .save(destination: Uri) are both (Uri) -> Unit, so swapping the two launcher callbacks at ConverterScreen.kt:70 and :83 compiles, renders, and passed the entire suite. Picking a file would attempt a save to it; choosing a destination would load it as input.

That is exactly the defect class ScreenWiringTest exists for, on the one pair it declines to cover — it drives converterActions directly and says the launcher-backed actions stay parameters. Right about the actions seam, and it leaves the edge above that seam unpinned.

Join's equivalents (JoinScreen.kt:45, :55) are List<Uri> and Uri, so they are not transposable and get no such test.

The mechanics were spiked before the assertions were written

Neither is used anywhere else in the suite, and #200 said to verify them first:

mechanic result
shadowOf(activity).nextStartedActivityForResult reads the launched Intent, EXTRA_MIME_TYPES intact
shadowOf(activity).receiveResult(...) reaches ComponentActivity's ActivityResultRegistry and fires the rememberLauncherForActivityResult callback

Both work. createAndroidComposeRule for AdaptiveShellTest's reason: the screens compose real ViewModels through viewModel(), and the plain rule supplies no ViewModelStoreOwner.

Picker filter rides along

ConverterScreen.kt:65-67 records why the all-types wildcard is load-bearing rather than lazy — the photo picker offers no audio at all and won't reliably surface .mkv/.flac/.webm. Narrowing it would make every audio conversion unreachable from the picker, and nothing would have gone red.

One incidental worth knowing: a KDoc cannot contain that wildcard, because its second half closes the block comment. The literal is spelled only in the assertion, and the KDoc says why.

Acceptance: mutations run and restored

mutation result
transpose onInputPicked and save red
narrow the converter picker to video only red
widen the join picker to every type red

Verification

testDebugUnitTest (full suite) + ktlintCheck + detekt + lintDebug — green, production tree clean.

🤖 Generated with Claude Code

Closes #200. ## The hazard `ConversionViewModel.onInputPicked(uri: Uri)` and `.save(destination: Uri)` are **both `(Uri) -> Unit`**, so swapping the two launcher callbacks at `ConverterScreen.kt:70` and `:83` compiles, renders, and **passed the entire suite**. Picking a file would attempt a save to it; choosing a destination would load it as input. That is exactly the defect class `ScreenWiringTest` exists for, on the one pair it declines to cover — it drives `converterActions` directly and says the launcher-backed actions stay parameters. Right about the `actions` seam, and it leaves the edge above that seam unpinned. Join's equivalents (`JoinScreen.kt:45`, `:55`) are `List<Uri>` and `Uri`, so they are **not** transposable and get no such test. ## The mechanics were spiked before the assertions were written Neither is used anywhere else in the suite, and #200 said to verify them first: | mechanic | result | |---|---| | `shadowOf(activity).nextStartedActivityForResult` | reads the launched `Intent`, `EXTRA_MIME_TYPES` intact | | `shadowOf(activity).receiveResult(...)` | reaches `ComponentActivity`'s `ActivityResultRegistry` and fires the `rememberLauncherForActivityResult` callback | Both work. `createAndroidComposeRule` for `AdaptiveShellTest`'s reason: the screens compose real ViewModels through `viewModel()`, and the plain rule supplies no `ViewModelStoreOwner`. ## Picker filter rides along `ConverterScreen.kt:65-67` records why the all-types wildcard is load-bearing rather than lazy — the photo picker offers no audio at all and won't reliably surface `.mkv`/`.flac`/`.webm`. Narrowing it would make every audio conversion unreachable from the picker, and nothing would have gone red. One incidental worth knowing: **a KDoc cannot contain that wildcard**, because its second half closes the block comment. The literal is spelled only in the assertion, and the KDoc says why. ## Acceptance: mutations run and restored | mutation | result | |---|---| | transpose `onInputPicked` and `save` | **red** | | narrow the converter picker to video only | **red** | | widen the join picker to every type | **red** | ## Verification `testDebugUnitTest` (full suite) + `ktlintCheck` + `detekt` + `lintDebug` — green, production tree clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.