M2-07: Recover a route the app was killed during #49
No reviewers
Labels
No labels
area/agent
area/capture
area/editor
area/geo
area/security
area/sync
area/transcription
area/ui
area/vault
area/voice-memos
blocked
milestone/M1
milestone/M2
milestone/M3
milestone/M4
needs-decision
needs-hardware
p0
p1
p2
type/bug
type/enhancement
type/feature
type/idea
type/infrastructure
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Stash/stash-ios!49
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "m2-07-track-recovery"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #34. Second of two changes; #48 landed the format, the writer and assembly at stop, and said in as many words that a route a crash interrupted was not swept yet.
The sweep
CaptureRecovery— the renamedTranscriptRecovery— runs once per process and finishes both halves of a capture, transcripts and then routes. Each clears the parts it owns and leaves the other's, so the shared folder is emptied by whichever runs second. The route half needs its own predicate: the transcript's keys on a missing transcript, so a capture whose transcript published cleanly and whose track did not looks finished to it.A route is published only while the opt-in still grants — asked per capture, and again on the line before the
link(2)that publishes, because the sweep is detached and reading the parts takes time. The active capture is asked for per candidate rather than snapshotted: a recording can begin while the transcript pass runs, and a stale snapshot would let this publish a partial track from a live folder and delete the parts out from under the writer.It replaces the withdrawal marker that shipped in #48
The ordering rule does not hold, in either direction, and that is worth stating plainly because it reads as clever. Deleting parts oldest first and inferring an interrupted withdrawal from a run that does not begin at 001:
TrackPartWritercarries on after a failed write — so a legitimate route would be deleted on the next launch as though consent had been withdrawn.The shape of a folder cannot say what happened to it.
<base>_track.withdrawnsays the one thing it cannot, it outranks the toggle being back on, and it is taken away at stop once the process has plainly survived the deletion.It is written synchronously at the instant consent goes, not when the deletion runs. The deletion has to wait for the points already on their way to the writer to drain, or one of them recreates a file just removed — and an app that exits inside that wait would otherwise leave route parts with nothing to say they were refused. That was the security review's one high finding, and it is the same class of hole as the ordering rule itself.
This supersedes the no-tombstone decision. The reason it does is that the premise that decision rested on — that the ordering covered the case — turned out not to hold.
Also
CapturePartsisTranscriptAssembly.clearPartsand its ownership predicates, moved to their own file. Three callers own different things in that folder now and one of them is a route being deleted because consent went; it is not the transcript's rule any more. The type-length lint is what prompted it, and it was right to.A refused route that will not delete is reported and not counted as removed — saying both "removed one route" and "part of it could not be removed" answers the one question that line exists for with two contradictory halves. A folder that cannot be listed is a failure rather than an empty folder. An assembly that produced no file is not a recovery.
What Codex caught
Four passes. Beyond the two above:
CaptureRecovery.sweepOnce, separate from theTaskthat is the single-flight, with a suite over it.activeBasewas asked once of one candidate; there are two captures now. Refused-route cleanup was only exercised with canonical part names, so the.sb-temporary and its AppleDouble sidecar are asserted too.Full dispositions in
.agent/m2-07-track-recovery/SUMMARY.md.Validated, and not
534 tests pass,
make lintclean,make runlaunches.Not tested on a device, which is what the
needs-hardwarelabel is for. Steps are inSUMMARY.md; note that step 4 turns location off in iOS Settings rather than in the app, because the in-app toggle cannot be reached while Stash is force-quit and reopening is what starts recovery.The marker's ordering is held by the code, not by a test. Whether it is written before the unlinks cannot be observed from outside the actor without interposing on the filesystem. What is tested is that a marker which cannot be written does not stop the deletion.