A save that finishes after Start over puts Saved back on a cleared screen #123
Open
opened 2026-08-26 03:37:58 +00:00 by JMR-dev
·
1 comment
No Branch/Tag Specified
main
fix/102-picker-back-press-overshoot
fix/268-saf-picker-determinism
feat/ogg-vorbis-libvorbis
feat/expedited-conversion-work
fix/gate-cache-in-worktrees
chore/gate-runs-shellcheck
test/publish-delete-arm-real-provider
docs/e8-instrumented-coverage
docs/api37-carrier-count-drift
docs/e7-second-constraint
test/publish-to-a-real-saf-destination
fix/api37-report-match-line
fix/api37-task-snapshot-crash
test/join-failure-message-on-device
docs/e2e-read-findings-e7
test/cancelling-a-running-export
test/reattach-to-a-running-job
test/content-uri-reaches-ffmpeg
fix/launcher-wiring-waits-for-the-pick
fix/cancel-tests-need-a-slower-encode
test/cancelling-a-running-join
test/cancelling-a-running-session
test/notification-cancel-action
test/ffmpeg-progress-is-observed
test/fallback-asserts-the-path
test/flac-and-opus-assert-their-format
docs/e2e-read-findings
docs/wave4-coverage-numbers
fix/injectable-startup-sweep-scope
test/session-outcome-seam
test/launcher-callback-identity
test/theme-follows-system-dark
test/audio-drop-arm
fix/rotation-waits-for-recreation
fix/convert-guards-on-ready
test/retry-save-mime
test/hardware-progress-reaches-workmanager
test/ffprobe-mapping-seam
test/device-codec-enumeration-seam
test/unknown-container-row
test/null-message-fallbacks
test/cancel-reaches-workmanager
docs/coverage-wave3-recovery
test/concat-engine-seam
docs/coverage-wave3
test/mediaprobe-merge-seam
test/adaptive-shell-wiring
test/aac-audio-args
test/notification-progress-text
test/media3-muxer-guard
test/hardware-fallback-and-cancellation
test/unprobeable-join-clip
test/one-branch-outcomes
test/foreground-type-regimes
fix/bound-wedge-diagnostics
docs/coverage-wave2
test/screen-wiring
test/viewmodel-setters
test/join-state-mapping
test/conversion-state-mapping
test/dedupe-user-messages
fix/restore-stack-merges
test/refused-jobs
test/concatworker-failure-arms
test/container-capabilities-audio
test/readspec-enum-fallbacks
test/outputpublisher-seams
test/mediaprobe-track-seam
test/outputpublisher-partial-branches
test/fake-provider-scaffolding
docs/coverage-read-findings
chore/gitignore-kotlin
test/bound-the-hangs
docs/coverage-remeasure
ci/baseline-counter-precision
fix/invalid-suggestion-chip
ci/wedged-leg-report
fix/reattachment-overwrites-pick
test/theme-live-branches
fix/failed-save-retry
fix/empty-composition-crash
ci/advisory-failure-report
docs/seven-run-counts
test/release-permission-guard
ci/build-workflow-permissions
docs/api37-point-release
docs/benchmark-populate-path
fix/dead-assertion-probe-test
ci/actionlint
test/device-codecs-encode-consequence
fix/sdkmanager-pipefail
docs/readme-restart-claim
fix/saf-picker-root-discovery
fix/probe-dispatcher-seam
test/media3engine-mime-tables
test/mediaprobe-pure-helpers
fix/codec-vocabulary-drift
docs/robolectric-rationale-correction
docs/api37-advisory-counts
test/r38-8-saf-e2e
test/r38-7-join-states
test/r38-6-conversion-states
test/r38-5-state-seam
fix/jacoco-robolectric-coverage
docs/instrumented-tests-correction
test/r38-2-filecard
test/r38-4-advanced-picker
test/r38-3-pickers
tools/file-issue-script
tools/api-37-emulator
fix/review-app-gaps
docs/review-corrections
No results found.
Labels
Clear labels
above-cut
accessibility
backlog
bug
confirmed
documentation
duplicate
enhancement
good first issue
help wanted
invalid
plausible
question
sev:high
sev:low
sev:medium
wontfix
Worked autonomously overnight: local, JVM-verifiable, no product decision
Barrier affecting people with disabilities
Held for manual review: product/UX call, CI/workflow, hardware, or unverifiable here
Something isn't working
Reviewer demonstrated the defect
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
Good for newcomers
Extra attention is needed
This doesn't seem right
Reviewer could not fully demonstrate it; treat as unproven
Further information is requested
High severity
Low severity
Medium severity
This will not be worked on
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: JMR-dev/LibreMediaConverter#123
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
A save that finishes after "Start over" puts
Savedback on a screen the user clearedFound while fixing #49, and deliberately not fixed there, because closing it means choosing
between two defensible answers and a race fix is the wrong place to decide that by accident.
The interleaving
ConversionViewModel.save(andJoinViewModel.save, identically) writes its result after a hop:The screen stays on
Convertedfor the whole of that copy, andConvertedrenders both "Save"and "Start over". So:
Dispatchers.IO.reset()runs on the main thread: state toIdle, staged file deleted.onSuccesswritesSaved(name)overIdle.The user asked for a blank screen and got a success message for work they had just dismissed.
Why #49's fix does not cover it
#49 added
ScreenOwnership: every deferred write checks the claim it was made under.saveis theone deferred write deliberately left out, and the KDoc on
ConversionViewModel.ownershipsays soand says exactly this much.
The reason it was left out is that guarding it is not obviously right.
publishmay genuinely havecopied the bytes to the user's destination before
reset()deleted the staged copy. Dropping thewrite reports nothing for a file that really was saved; keeping it reports success for something the
user dismissed. Neither is clearly the lesser evil.
What a fix probably looks like
The question is about what the screen should offer during a save, not about the write itself:
rather than arbitrating it), or
silent, or
Savedreachable fromIdleas a transient message rather than a state.The first looks likeliest to be right, and it is a UI change rather than a ViewModel one.
Testing note
ScreenOwnership's claim inreset()is currently unreddenable by the JVM suite, and that isthe same fact as this ticket: the only deferred write that could land on top of a
reset()issave's, andsaveis the exempt one. Whatever closes this should also make that claim bite —RecordingPublisheris subclassable and apublishthat blocks on a latch is enough to constructthe interleaving deterministically, the same way
ParkedPickDispatcherdoes for a pick.Verified on
main(b49295d), and there is a second bad outcomeThe interleaving is real on
maintoday, independently of #124.save():reset():So the deferred write is unguarded on both arms, and the ticket's scenario describes only the
success one.
The failure arm is the worse case.
reset()does not merely setIdle— it launchesdiscardStaged(staged)oncleanupDispatcherwhilepublishmay still be reading that same file.If the delete wins,
publishthrows, and the user gets a red error message on a screen theydeliberately cleared, for a save they cancelled. That is strictly more alarming than a stray
success, and it is reachable by the same three taps.
It also means the two dispatchers are racing over the file itself, not just over
_state. Whicheveroption is chosen, that needs an answer too — "disable Start over while a save is in flight" happens
to close both, which is a point in its favour beyond the one the ticket gives.
On the unreddenable claim
The ticket's closing note is the important part and should not get lost:
ScreenOwnership's claim inreset()cannot currently be reddened by the JVM suite, becausesaveis the only deferred writethat could land on a
reset()andsaveis the exempt one. That is an assertion with no mutationbehind it — the exact shape this repo has measured before (9 of 46 mutations vacuous, five over a
completely unguarded path).
Disclosing it rather than letting it read as covered is the right call. Whatever closes this ticket
should make that claim bite, via a
RecordingPublishersubclass whosepublishblocks on a latch —the same construction
ParkedPickDispatcheralready uses for a pick.