NEC-288: Excalidraw image element's file id loses its Embedded Files line after an offline move #297

Merged
nectenda-agent merged 7 commits from worktree-nec-288-excalidraw-image-elements-file-id-loses into main 2026-10-15 21:38:04 +01:00
Collaborator

Task: NEC-288 — https://projectron.nerchure.com/tasks/301

Fixes three ways an image in a synced Excalidraw drawing lost its link under "## Embedded Files" and showed as an empty frame, all found by the Excalidraw hardness run.

  • NEC-288: Excalidraw gives an image a new file id when its attachment hasn't arrived, without raising the element's version. When that save was a closing tab's, the closing-tab guard (NEC-235) held the new link back, and the next edit carried the image out without it. A link the document lacks, for an image the same save draws, is now read in. A changed link (a rename elsewhere) is still held back.
  • NEC-291 (ride-along): a bound view that hasn't loaded an image's picture saves without its link. The document kept the link (NEC-281), but the vault's own file never got it back. The binding now lets go so the file is rewritten, at most once every 30 seconds per link.
  • Concurrent tidy-up: a link the document lacks, for an image it now draws, is read in from a save whatever the base says. This covers another vault tidying an unused link away just as an edit started using it.

Verified: unit tests for each rule, mutation-checked both ways; the local gate at cb464b8e (unit, typecheck, lint, mirror check, 230 container e2e). Hardness: about 45 minutes since the last fix (the user's call). Failing seeds 1058836011, 87359779 and 1474780160 replay clean past where they failed. There's no e2e test for the original bug, since real Excalidraw wouldn't reproduce it on demand (see the spec). Two fresh-context reviews; everything they confirmed is fixed. Filed separately: NEC-294 (P1), an intermittent lost offline edit during a sync restart, not caused by this change.

Spec: docs/changes/NEC-288-excalidraw-image-file-id-link/spec.md

Also fixes NEC-291: An image's Embedded Files link, saved while sync was stopped, never reaches the document

Changelog

Fixed images in synced Excalidraw drawings sometimes showing as empty frames after being moved, inserted while another vault had the drawing open, or edited offline.

Task: NEC-288 — https://projectron.nerchure.com/tasks/301 Fixes three ways an image in a synced Excalidraw drawing lost its link under "## Embedded Files" and showed as an empty frame, all found by the Excalidraw hardness run. - **NEC-288:** Excalidraw gives an image a new file id when its attachment hasn't arrived, without raising the element's version. When that save was a closing tab's, the closing-tab guard (NEC-235) held the new link back, and the next edit carried the image out without it. A link the document lacks, for an image the same save draws, is now read in. A changed link (a rename elsewhere) is still held back. - **NEC-291 (ride-along):** a bound view that hasn't loaded an image's picture saves without its link. The document kept the link (NEC-281), but the vault's own file never got it back. The binding now lets go so the file is rewritten, at most once every 30 seconds per link. - **Concurrent tidy-up:** a link the document lacks, for an image it now draws, is read in from a save whatever the base says. This covers another vault tidying an unused link away just as an edit started using it. Verified: unit tests for each rule, mutation-checked both ways; the local gate at cb464b8e (unit, typecheck, lint, mirror check, 230 container e2e). Hardness: about 45 minutes since the last fix (the user's call). Failing seeds 1058836011, 87359779 and 1474780160 replay clean past where they failed. There's no e2e test for the original bug, since real Excalidraw wouldn't reproduce it on demand (see the spec). Two fresh-context reviews; everything they confirmed is fixed. Filed separately: NEC-294 (P1), an intermittent lost offline edit during a sync restart, not caused by this change. Spec: `docs/changes/NEC-288-excalidraw-image-file-id-link/spec.md` Also fixes NEC-291: An image's Embedded Files link, saved while sync was stopped, never reaches the document ## Changelog Fixed images in synced Excalidraw drawings sometimes showing as empty frames after being moved, inserted while another vault had the drawing open, or edited offline.
Excalidraw gives an image that shares a file id whose attachment has not
arrived an id of its own as it saves, with a link for it, and does not raise
the element's version. When that save was a tab's as it turned to another
drawing, the closing-tab guard (NEC-235) took the new link for what the tab
had shown and held it back; the next edit carried the element out without
it, and the image was an empty frame in every vault. Found by the
Excalidraw hardness run, seed 1058836011.

Task: NEC-288

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017L7qQ7uLi23M7Rhgudvrkt
Review found the rule too broad: a closed tab's link for an id its image
draws was read in even where the document held another link for that id,
so a rename made in another vault could be set back, silently and
everywhere. It now applies only where the document has no link for the id,
and such a link takes the path of any change read in, so an undo of it
within the 30 seconds is read in too. The test meant to go through the
binding now binds before the closing tab's save lands, and says so.

Task: NEC-288

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017L7qQ7uLi23M7Rhgudvrkt
Task: NEC-288

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017L7qQ7uLi23M7Rhgudvrkt
A view that has not loaded an image's picture saves without the image's
link. The document keeps the link while it draws the image (NEC-281), but
while bound only the view writes the file, so that vault's file went on
without it and the image stayed an empty frame there. Found by the
Excalidraw hardness run, seed 87359779: a 3 MB image inserted in A, drawn
in B before its attachment arrived. The binding now lets go, so the file is
written with the document's link and the plugin loads it.

Debug lines for links a read-in removes or leaves out stay in the
diagnostic log: they are what found this.

Task: NEC-288
Also-fixes: NEC-291

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017L7qQ7uLi23M7Rhgudvrkt
Excalidraw gives an image an id of its own without raising its version,
so its link can sit in the document with no image naming it. Another vault
tidied 41 such links away just as a move here carried an image out under
one of them, and this vault's next save, naming the link as its base did,
diffed to no change and never put it back. Found by the Excalidraw
hardness run, seed 1474780160, round 96. The mirror of the rule that keeps
a link a save leaves out while the document draws its image.

Task: NEC-288

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017L7qQ7uLi23M7Rhgudvrkt
Let go for a left-out link at most once in 30 seconds
Some checks failed
CI / promote (pull_request) Has been cancelled
CI / e2e (pull_request) Has been cancelled
CI / build (pull_request) Has been cancelled
Release note / release-note (pull_request) Successful in 16s
cb464b8e68
Review found the let-go for a link a bound save leaves out had no end: an
attachment that never arrives left the view saving without the link for
good, and the vault let go and reloaded on every save, never live. The
record of when each link was last let go for is kept on the witness, since
each rebind is a new binding. A debug line that ran on every capture is
removed, and a narrow out-of-date-link case is recorded as a limitation.

Task: NEC-288
Also-fixes: NEC-291

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017L7qQ7uLi23M7Rhgudvrkt
Release notes for the two image fixes
All checks were successful
Release note / release-note (pull_request) Successful in 17s
CI / build (pull_request) Successful in 5m24s
CI / e2e (pull_request) Successful in 12m15s
CI / promote (pull_request) Has been skipped
CI / build (push) Successful in 6m8s
CI / e2e (push) Successful in 12m24s
CI / promote (push) Successful in 33s
903d5f21aa
Task: NEC-288
Also-fixes: NEC-291

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017L7qQ7uLi23M7Rhgudvrkt
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Nectenda/nectenda!297
No description provided.