mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
v1.46.4: detached plans were being collected as garbage — data loss
Deployed v1.46.3 to my own instance, restarted, and the startup sweep deleted
both floor plans: config/houseplan/plans/ went from f1.svg + f2.png to empty.
The backup is a SecureTar, so they are gone.
The rule was wrong, not the code. v1.46.0 introduced collection that treats
'nothing references this right now' as abandoned and gives it an hour. But
detaching a plan — switching a space to 'draw' — is a normal, reversible action,
and the editor's own comment says the file stays on disk. Those two plans had
been detached for weeks; every pass since v1.46.0 was entitled to remove them,
and the one that finally ran did.
New rule, one for every path:
* superseded by a commit (was in the old revision, is not in the new) — goes
immediately; that is the one thing a commit knows for certain;
* belongs to a space or marker that still exists — never collected, at any
age, because unreferenced is not abandoned;
* a per-dialog staging folder (up_*) — one hour, unchanged: by construction it
only ever holds an upload from a dialog that was never saved;
* anything else — thirty days.
The flag I added an hour ago is gone with it: two rules for the same
question is how this happened. Tests updated to the new grace, plus two that pin
the distinction directly.
I am sorry about the files.
This commit is contained in:
@@ -207,8 +207,15 @@ happen when somebody saves, and a file uploaded into a dialog that was then
|
||||
cancelled would wait for a write that may never come. A new icon has no id yet, so its
|
||||
files go to a per-dialog staging folder and move to the real id once the config
|
||||
write is accepted — the same copy → save → cleanup order as a rebind.
|
||||
`config/set` collects what its commit superseded, and anything unreferenced and
|
||||
older than `PLAN_ORPHAN_TTL_S`.
|
||||
`config/set` collects what its commit superseded — and only that, because
|
||||
supersession is the one thing a commit knows for certain. *Unreferenced* is a
|
||||
much weaker signal: detaching a plan is reversible and the editor promises the
|
||||
file stays. So a file belonging to a space or marker that still exists is never
|
||||
collected, and anything else waits `SCHEDULED_GRACE_S` (30 days). The exception
|
||||
is a per-dialog staging folder, which by construction only holds an upload from
|
||||
a dialog that was never saved — `PLAN_ORPHAN_TTL_S` (1 hour) is right there.
|
||||
This is not theoretical caution: the one-hour rule applied to unreferenced files
|
||||
destroyed two detached plans on 2026-07-28.
|
||||
|
||||
**Config writes are serialized** (HP-1454-03). `_writeConfig()` chains onto a
|
||||
single promise: one `config/set` in flight, each carrying the revision the
|
||||
|
||||
Reference in New Issue
Block a user