uttrflow / uttrflow/uttrflow-swift
Copying a picture again does not restore its missing image file
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## Problem
If a picture file disappears while its clipboard record remains, copying the same picture again does not repair it. `ClipboardStore.record(_:keeping:)` finds the existing SHA through `alreadyKept` and skips `keep`, even though the referenced file is missing. It then merges the arrival into the old record and increments `timesCopied`. The new copy supplies the bytes needed to recover, but they are discarded.
The history entry remains unusable for pasting from the panel. The current system clipboard may still contain the newly copied picture; this finding concerns the saved history entry and its backing file.
## Reproduction
Against unmodified main `8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d`, using the production store in a temporary directory:
1. Record a `NoticedClip` containing picture bytes.
2. Delete only the resulting PNG file, preserving the index and live store.
3. Record another `NoticedClip` with identical picture bytes and a fresh clip ID.
4. Read the resulting record and call `imageData(for:)`.
Observed: one merged record, `timesCopied == 2`, but `imageData(for:) == nil` instead of the supplied bytes. The recovery assertion failed. All **50 existing watcher, marker, image, and unreadable-index tests** passed in the same run.
The probe uses synthetic bytes because the store does not decode PNG data. It performs real file writes/deletion and calls the actual hashing, deduplication and persistence code. File deletion is injected to exercise recovery; this does not claim the app normally deletes referenced files.
## Relevant code
- `Sources/UttrflowClipboard/ClipboardStore.swift`: `record(NoticedClip, keeping:)`, `alreadyKept`, and `inheriting` reuse the old image metadata without checking that the bytes still exist.
- `Tests/UttrflowClipboardTests/ClipImageTests.swift`: `vanishedFile` already covers a missing file and intentionally retains its record, but does not recopy it.
- `Tests/UttrflowClipboardTests/ClipboardDedupeTests.swift`: `picturesMergeOnTheirBytes` covers the healthy-file case only.
This differs from #582/#584/#585, which concern reporting a missing or failed picture paste. This issue is about restoring the history entry when its bytes become available again.
## Acceptance criteria
- An identical picture copied again restores a missing backing file before the store reports success.
- Preserve the clip ID, alias, category, pin status, and repeat count when repairing it.
- Continue avoiding duplicate files when the existing file is healthy.
- A refused repair write reports `couldNotWrite`; add coverage for both successful repair and failure.
Priority: P2 — recovery and clipboard reliability.
Contributor guide
Research direction
Start in Sources/UttrflowClipboard/ClipboardStore.swift by tracing record(_:keeping:), alreadyKept, and inheriting. Read the vanishedFile test in Tests/UttrflowClipboardTests/ClipImageTests.swift and the healthy deduplication coverage in ClipboardDedupeTests.swift, then run the existing suite. Done means recopying restores a missing file, preserves metadata, avoids duplicate healthy files, and covers successful and refused repair writes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100