fix: a refresh takes every field the fresh read decides

`refreshBlanks` merged only `missing` out of the new scan, so the two fields
added alongside it were left at whatever the row said before. Both reviewers
found the same seam from opposite ends: a resource that had just become
unreadable kept its old readable-looking row, and one that had come back
stayed blocked until a reload.

These fields describe what is at the path now, so the fresh read owns all of
them — and the branch that marks a row done clears them, because a row that
has left the blank list was read and is filled.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xg8vXUuHCH3aRkf91sfxjx
This commit is contained in:
Guilhem Lemouel
2026-08-25 18:11:29 +02:00
parent 5d695f8546
commit e49b4f1133
@@ -339,8 +339,29 @@
}
blanks = blanks.map((b) => {
const f = stillBlank.get(b.path)
if (f) return { ...b, missing: f.missing, done: false, justSaved: false }
return { ...b, missing: [], done: true, justSaved: !b.done }
// Every field the fresh read decides is taken from it, not merged selectively: these
// describe what is at the path *now*. Keeping a stale `unreadable` leaves a resource
// that has since come back blocked until a reload, and keeping a stale absence hides
// one that has just become unreadable.
if (f) {
return {
...b,
missing: f.missing,
unreadable: f.unreadable,
occupiedBy: f.occupiedBy,
done: false,
justSaved: false
}
}
// Gone from the blank list entirely: it was read, and it is filled.
return {
...b,
missing: [],
unreadable: undefined,
occupiedBy: undefined,
done: true,
justSaved: !b.done
}
})
// The flash is a one-shot; clear it so a later refresh does not replay it.
for (const b of blanks) {