NEC-277: NEC-268 stage B: structured-sync and canvas onto the provenance ledger #307

Merged
nectenda-agent merged 10 commits from worktree-nec-277-nec-268-stage-b-structured-sync-and-canv into main 2026-10-19 13:56:29 +01:00
Collaborator

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

Stage B of NEC-268. Structured files (canvases, drawings, bases) and canvas-live move onto the provenance ledger stage A gave notes: one ledger per file, read by every reader through the same classifier, a structured file's texts compared by meaning. It replaces seven pieces of state (lastSyncedText, diskSeen, writingText, released, baseFromRecord, the file record, canvas-live's serialised). The write decides on one read and checks the ledger's generation after each await instead of reading the file again, which closes the A-B-A case. A view's save is noted only to tell it from an outside rewrite, never as proof the document holds it; a let-go text is the merge base only at a connect's first write, where both readings are kept when they disagree; another file's content kept aside never becomes a base. file-record.ts keeps only the migration parser. The review found and this fixes a bind against the let-go text and a stored let-go outliving the next session's agreements; the canvas hardness run found and this fixes in-session read-ins re-setting unchanged fields (seed 909117678). SAFE-A35 and SAFE-C1 reworded, sync-limitations updated, spec in docs/changes/NEC-277-structured-ledger/spec.md. Verified: local gate, mutation checks for every new rule, canvas hardness 90 min on the final code, Excalidraw hardness 60 min (2.28.1 vs 2.27.3), and the user's own vaults.

Spec: docs/changes/NEC-277-structured-ledger/spec.md

Changelog

Canvases, drawings and other structured files now track what they last agreed with their file in one place, so edits made outside Obsidian or while sync is stopped are merged against the right version, and an ambiguous merge keeps copies of both readings.

Task: NEC-277 — https://projectron.nerchure.com/tasks/288 Stage B of NEC-268. Structured files (canvases, drawings, bases) and canvas-live move onto the provenance ledger stage A gave notes: one ledger per file, read by every reader through the same classifier, a structured file's texts compared by meaning. It replaces seven pieces of state (lastSyncedText, diskSeen, writingText, released, baseFromRecord, the file record, canvas-live's serialised). The write decides on one read and checks the ledger's generation after each await instead of reading the file again, which closes the A-B-A case. A view's save is noted only to tell it from an outside rewrite, never as proof the document holds it; a let-go text is the merge base only at a connect's first write, where both readings are kept when they disagree; another file's content kept aside never becomes a base. file-record.ts keeps only the migration parser. The review found and this fixes a bind against the let-go text and a stored let-go outliving the next session's agreements; the canvas hardness run found and this fixes in-session read-ins re-setting unchanged fields (seed 909117678). SAFE-A35 and SAFE-C1 reworded, sync-limitations updated, spec in docs/changes/NEC-277-structured-ledger/spec.md. Verified: local gate, mutation checks for every new rule, canvas hardness 90 min on the final code, Excalidraw hardness 60 min (2.28.1 vs 2.27.3), and the user's own vaults. Spec: `docs/changes/NEC-277-structured-ledger/spec.md` ## Changelog Canvases, drawings and other structured files now track what they last agreed with their file in one place, so edits made outside Obsidian or while sync is stopped are merged against the right version, and an ambiguous merge keeps copies of both readings.
Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
One ledger per structured file, read by every reader through the classifier
the notes use, compared by meaning. It replaces lastSyncedText, diskSeen,
writingText, released, baseFromRecord, the file record, and canvas-live's
serialised. The write decides on one read and checks the ledger's generation
after each await, where it used to read the file again. A view's save is
noted only to tell it from a rewrite, never as proof the document holds it.
file-record.ts keeps only the parser a ledger is migrated from.

Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
A save that may be built on what a view held as it let go, or on the file
before it, and merges differently against them, is kept with the document as
the other reading would leave it: a card the view showed and no file held is
deleted by the reading taken, and a copy of the file alone does not hold it.

Tests for the structured ledger: one decision read, a file put back during
the put, another file's content never a base, a view's save after the one
before it was agreed, both readings kept, a let-go text across a restart and
once passed, and an older record migrated and retired. Each mutation-checked.

Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
Found by review. A view binding just after another let go was diffed against
what the other view held, and a card the file never had read as deleted
everywhere. An earlier session's let-go text outlived every agreement, so a
file put back to it much later was written over. Within a session the two
readings of a save were kept only after its read-in; now before it, and with
nothing read in or written over when they cannot be kept. A modify event
equal to the file's last state is nothing new.

Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
Counts alone could not tell which write a concurrent resize took. Ids and
field names only.

Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
Against what a view held as it let go, a save re-set every field the view had
drawn differently, a size Obsidian adjusted as it rendered, to the value the
document already had, as new entries of this vault's. A resize made in the
other vault at that moment took them for edits lost and kept a conflict copy
of nothing (canvas hardness run, seed 909117678, twice in three replays; main
passed it). The let-go text is the base only at a connect's first write, as
the record's was, where both readings are still kept when they disagree.

Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
Record the hardness runs in NEC-277's spec
All checks were successful
Release note / release-note (pull_request) Successful in 20s
CI / build (pull_request) Successful in 6m3s
CI / e2e (pull_request) Successful in 12m47s
CI / promote (pull_request) Has been skipped
CI / build (push) Successful in 6m53s
CI / e2e (push) Successful in 12m51s
CI / promote (push) Successful in 35s
49bdb975bf
Task: NEC-277
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HSAzoEhNtWx5yNX5fifhS9
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!307
No description provided.