NEC-315: Drawing edits saved while sync was stopped are written over, with no copy, by the next connect's first write #309

Merged
nectenda-agent merged 9 commits from worktree-nec-315-drawing-edits-saved-while-sync-was-stopp into main 2026-10-20 13:35:08 +01:00
Collaborator

Task: NEC-315 — https://projectron.nerchure.com/tasks/335

The hardness run's lost drawing edits (seed 1461695148) were not written over by sync: instrumenting the connect's first write and watching the file across the quit showed Excalidraw's save on close cut off part-way, truncating the earlier save with the edits before sync could read it. This adds a notice when a connect finds a structured file empty or unreadable though its record held content (SAFE-A47), keeps the approved SAFE-A46 net (a connect's first write keeps a drawing holding element versions its document lacks), permanent diagnostic logging of the first write's decision and every ledger move, and fixes the hardness harness to save before a quit, report a cut-off save, and judge lost offline work shape by shape against the file. The limitation is recorded in docs/sync-limitations.md. Local gate green; 80 minutes of Excalidraw hardness passed over four seeds (A 2.28.1, B 2.27.3).

Spec: docs/changes/NEC-315-drawing-edits-overwritten-at-connect/spec.md

Changelog

Nectenda now tells you when it starts and finds a drawing or canvas emptied or cut off, as a save interrupted by Obsidian closing can leave it, instead of silently replacing it.

Task: NEC-315 — https://projectron.nerchure.com/tasks/335 The hardness run's lost drawing edits (seed 1461695148) were not written over by sync: instrumenting the connect's first write and watching the file across the quit showed Excalidraw's save on close cut off part-way, truncating the earlier save with the edits before sync could read it. This adds a notice when a connect finds a structured file empty or unreadable though its record held content (SAFE-A47), keeps the approved SAFE-A46 net (a connect's first write keeps a drawing holding element versions its document lacks), permanent diagnostic logging of the first write's decision and every ledger move, and fixes the hardness harness to save before a quit, report a cut-off save, and judge lost offline work shape by shape against the file. The limitation is recorded in docs/sync-limitations.md. Local gate green; 80 minutes of Excalidraw hardness passed over four seeds (A 2.28.1, B 2.27.3). Spec: `docs/changes/NEC-315-drawing-edits-overwritten-at-connect/spec.md` ## Changelog Nectenda now tells you when it starts and finds a drawing or canvas emptied or cut off, as a save interrupted by Obsidian closing can leave it, instead of silently replacing it.
A drawing's edits made while sync was stopped were written over, with no
copy, by the next connect's first write, and no line said which recorded
text the file had matched or which reader had recorded it. Each ledger
move now names its caller; the ledger is described as it loads and as the
first write decides; the first write says how it ended; and a note saved
while sync is stopped is logged by short key, so the save and the file
the connect later finds can be compared. Short keys only, never content.

Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
A connect's first write replaces a file it calls behind its document with
no copy. For a drawing, that call rests on one byte-equal match with a
recorded text, and a drawing's edits saved while sync was stopped were
written over that way, in every vault, with nothing kept (NEC-315). The
drawing's own versions can say what the file holds that the document
lacks: an element it never had, or a version newer than any it knows of.
A file holding one is kept in .nectenda-backups before the write, whatever
the record says (SAFE-A46). A safety net, not the cause: that is next.

Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
The loss NEC-315 chased was not sync's: watched from outside Obsidian, an
Excalidraw save caught by the quit truncated the drawing to one 8 KiB
chunk, or to nothing, taking the save before it and its edits with it,
while sync was stopped. The connect then put the shared version back
without a word. A connect that finds a structured file empty or
unreadable, though its record holds content, now says so, and where the
file as found was kept (SAFE-A47); a placeholder is not announced.
SAFE-A46's rationale no longer claims a match that never happened, and
the limitation is recorded as it is.

Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
The quit deletes the session two seconds in, and an Excalidraw save it
caught was cut off part-way, destroying the save before it: seed
1461695148 failed round 12 on the quit, not on sync (NEC-315). The quit
now saves the open drawing and waits for a whole file first, watches the
file across the quit, and says in the trace if a save is cut off anyway.
NECTENDA_E2E_HARDNESS_DUMP_ROUND=<n> keeps round n's logs though it
passed, so a replay that stopped failing can still be compared.

Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
- The cut-off notice (SAFE-A47) is held on the file until a write lands. A
  first write refused after keeping the file was retried as an ordinary
  write, which did not know, and the user was never told.
- The safety net (SAFE-A46) looks only where the record called the file
  behind or equal. A file read as edited was kept by its merge, and the
  net kept it again, warning of a "behind" that never was.
- lackingElements' comment, and SAFE-A46, own the one case a file behind
  the document still counts: the losing rival of a version tie.
- The hardness quit waits until the file holds what the view shows,
  version for version, and the round's allowance for work lost to a quit
  applies only when the drawing did not save whole or a save was cut off.
  Allowed anyway, a connect writing over saved offline work passed.

Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
A move made offline with no save, then a reopen, left the open drawing
without it; the quit then saved whole, file and view agreeing, and the
run read the move's absence as sync's loss (seed 1939626087, round 93).
What sync must keep is what the file held as Obsidian quit, so each
shape's offline work is now checked against that file, and the trace
names what it did not hold.

Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
Record NEC-315's hardness hour in its spec
All checks were successful
Release note / release-note (pull_request) Successful in 22s
CI / build (pull_request) Successful in 6m4s
CI / e2e (pull_request) Successful in 12m28s
CI / promote (pull_request) Has been skipped
CI / build (push) Successful in 6m3s
CI / e2e (push) Successful in 12m24s
CI / promote (push) Successful in 32s
7a4a25ce4f
Task: NEC-315
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdjUzs3sXL6RcN7Rrqg9wB
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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!309
No description provided.