Files
tuliprox/frontend
DarkBreakpoint 3606257439 feat(dvr)!: cancelling is a state, not an instant
Task 11, step 2. Cancelling a running recording marked it `Cancelled`
immediately, while the worker was still writing the file and holding the
provider slot. `Cancelled` is terminal, so the recording became
deletable at once -- and deleting a cancelled entry unlinks its
`.partial`, which is the file the worker had open. The transfer went on
streaming into an unlinked inode.

`Cancelling` is now the state between asking and the worker letting go.
It is deliberately not terminal, so deletion is refused until the
teardown finishes. `transition` returns it only from the states where a
worker actually owns the file -- Running, WaitingForCapacity,
RetryWaiting. Cancelling a Queued, Scheduled or Paused recording has
nothing to wait for and stays immediate, because a state nothing drives
would be worse than no state at all.

Restart resolves it: the worker that was going to acknowledge died with
the process, so recovery commits `Cancelled`. Without that the entry is
not `finished`, and the requeue path would have restarted work the user
had cancelled.

Writing the test for this surfaced a worse bug, present since the split
made files shareable. Execution state lived on the materialization,
which two entries share. `join` read `state`, `partition`, `finished`,
`paused`, `error` and the retry fields from it, so whichever entry was
committed last decided what every entry looked like on the next load --
one user running a transfer silently rewrote another user's queued entry
as running, or the reverse. It is invisible until a restart.

Execution state moves to `PersistedLibraryEntry`, where the plan's Task 7
always said it belonged and I had omitted it. The materialization keeps
only what is genuinely one-per-file: path, size, url, kind, identity.
Two entries on one file can now disagree about their own lifecycles,
which is the entire point of them being separate entries.

Consequence worth knowing: a repository written before this commit
decodes with the new entry fields defaulted, so entries come back
`Queued`. The plan rules out migrating old DVR state and none of this
has shipped, so that is accepted rather than migrated.
2026-09-01 13:07:42 -05:00
..
2026-08-24 20:14:47 +02:00
2026-08-27 15:36:23 +02:00
2025-08-07 19:46:49 +02:00