NEC-105: Unacknowledged pushes: survive a restart, retry a failed delta, stop the oversized-push reconnect loop #171

Merged
nectenda-agent merged 2 commits from worktree-nec-105-unacknowledged-pushes-survive-a-restart into main 2026-09-25 15:29:58 +01:00
Collaborator

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

Closes the three edges NEC-18 left open around unacknowledged pushes, plus a wider cause found on the way. (1) The seq checkpoint is never stored while a document owes the server anything, pre-fix checkpoints are no longer read, and a catch-up from 0 always reconciles: it used to test lastSeq === 0 after the catch-up had raised it, so any launch without a trusted checkpoint pushed nothing and read as synced (SAFE-A10). (2) A reconnect delta whose encrypt fails is retried on the live socket, 2 s doubling to 60 s. (3) Decided with the user: the server push limit rises from 4 MiB to the 16 MiB document ceiling, catch-up frames are bounded by bytes as well as count (WIRE-044), and the client never sends a push over the limit; it shows an error naming size and limit and re-attempts on the next edit, reconnect or launch (SAFE-A11). An independent review found three further routes (a checkpoint during a slow cold-start catch-up, a save timer outliving destroy, a delta clearing a newer oversize hold); each is fixed with a regression test from its repro. Every new rule mutation-checked; local gate green at 92f36d7 including the multi-vault e2e. Spec: docs/changes/NEC-105-unacked-pushes/spec.md.

Spec: docs/changes/NEC-105-unacked-pushes/spec.md

Changelog

Edits the server never confirmed are now sent again after Obsidian restarts, notes up to 16 MiB now sync, and a change too large to send shows an error on the note instead of retrying in a loop.

Task: NEC-105 — https://projectron.nerchure.com/tasks/105 Closes the three edges NEC-18 left open around unacknowledged pushes, plus a wider cause found on the way. (1) The seq checkpoint is never stored while a document owes the server anything, pre-fix checkpoints are no longer read, and a catch-up from 0 always reconciles: it used to test lastSeq === 0 after the catch-up had raised it, so any launch without a trusted checkpoint pushed nothing and read as synced (SAFE-A10). (2) A reconnect delta whose encrypt fails is retried on the live socket, 2 s doubling to 60 s. (3) Decided with the user: the server push limit rises from 4 MiB to the 16 MiB document ceiling, catch-up frames are bounded by bytes as well as count (WIRE-044), and the client never sends a push over the limit; it shows an error naming size and limit and re-attempts on the next edit, reconnect or launch (SAFE-A11). An independent review found three further routes (a checkpoint during a slow cold-start catch-up, a save timer outliving destroy, a delta clearing a newer oversize hold); each is fixed with a regression test from its repro. Every new rule mutation-checked; local gate green at 92f36d7 including the multi-vault e2e. Spec: docs/changes/NEC-105-unacked-pushes/spec.md. Spec: `docs/changes/NEC-105-unacked-pushes/spec.md` ## Changelog Edits the server never confirmed are now sent again after Obsidian restarts, notes up to 16 MiB now sync, and a change too large to send shows an error on the note instead of retrying in a loop.
A push the server dropped was forgotten when the app quit: the read position
was stored while it was outstanding, recording it as accounted for, and a
launch without a trusted checkpoint never reconciled because it tested
lastSeq === 0 after the catch-up had raised it. Now the checkpoint is never
stored while anything is unconfirmed, old checkpoints are not read, and a
catch-up from 0 always reconciles (SAFE-A10).

A reconnect delta whose encrypt fails is retried on the live socket, 2 s
doubling to a minute. A push over MAX_PUSH_BYTES is never sent: the note shows
an error naming size and limit, keeps the work, and tries again on the next
edit, reconnect or launch (SAFE-A11). The push limit rises from 4 to 16 MiB,
the document ceiling, so a large note's first sync goes through, and catch-up
frames are bounded by bytes as well as count (WIRE-044).

Task: NEC-105
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012MCWWbpGZfyDgfuaxS9xmJ
Close three routes a review found around the NEC-105 rules
All checks were successful
Release note / release-note (pull_request) Successful in 31s
CI / build (pull_request) Successful in 4m47s
CI / e2e (pull_request) Successful in 5m36s
CI / promote (pull_request) Has been skipped
92f36d7d58
A cold start's first catch-up frame armed the checkpoint save, and content
restored from IndexedDB sits in no queue, so a slow catch-up stored a read
position covering work the reconcile had not sent yet. Not being caught up
now counts as owing the server (SAFE-A10).

A save armed before destroy fired afterwards, when the close had already
cleared the Ack count and could not mark a document it no longer held. The
timer is cleared with the subscription and checks it still belongs to it.

A reconnect delta that finished after a flush held back a newer oversized
change cleared that hold, so the note read "sending" instead of the error.
A delta now clears only holds placed before it was computed (SAFE-A11).

The large-push server test now pushes at the 16 MiB boundary and checks the
live relay to a peer.

Task: NEC-105
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012MCWWbpGZfyDgfuaxS9xmJ
cruelacid force-pushed worktree-nec-105-unacknowledged-pushes-survive-a-restart from 92f36d7d58
All checks were successful
Release note / release-note (pull_request) Successful in 31s
CI / build (pull_request) Successful in 4m47s
CI / e2e (pull_request) Successful in 5m36s
CI / promote (pull_request) Has been skipped
to ac732bb29d
Some checks failed
Release note / release-note (pull_request) Successful in 13s
CI / e2e (pull_request) Failing after 4m17s
CI / build (pull_request) Successful in 4m33s
CI / promote (pull_request) Has been skipped
2026-09-25 14:25:27 +01:00
Compare
cruelacid force-pushed worktree-nec-105-unacknowledged-pushes-survive-a-restart from ac732bb29d
Some checks failed
Release note / release-note (pull_request) Successful in 13s
CI / e2e (pull_request) Failing after 4m17s
CI / build (pull_request) Successful in 4m33s
CI / promote (pull_request) Has been skipped
to 38010cb182
All checks were successful
Release note / release-note (pull_request) Successful in 12s
e2e / multi (push) Successful in 4m42s
CI / build (pull_request) Successful in 5m6s
CI / e2e (pull_request) Successful in 5m28s
CI / promote (pull_request) Has been skipped
2026-09-25 15:17:06 +01:00
Compare
cruelacid force-pushed worktree-nec-105-unacknowledged-pushes-survive-a-restart from 38010cb182
All checks were successful
Release note / release-note (pull_request) Successful in 12s
e2e / multi (push) Successful in 4m42s
CI / build (pull_request) Successful in 5m6s
CI / e2e (pull_request) Successful in 5m28s
CI / promote (pull_request) Has been skipped
to 1eaad4b308
All checks were successful
Release note / release-note (pull_request) Successful in 12s
CI / build (pull_request) Successful in 4m30s
CI / e2e (pull_request) Successful in 5m10s
CI / promote (pull_request) Has been skipped
CI / build (push) Successful in 4m43s
CI / e2e (push) Successful in 5m9s
CI / promote (push) Successful in 31s
2026-09-25 15:23:57 +01:00
Compare
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!171
No description provided.