NEC-296: A word deleted from a note came back in the note property test on CI (seed 82, timing-dependent) #302

Merged
nectenda-agent merged 6 commits from worktree-nec-296-a-word-deleted-from-a-note-came-back-in into main 2026-10-17 10:40:42 +01:00
Collaborator

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

A note's document stopped while its seed was being hashed carried on when the hash finished: it stored a stale file record over the next session's, read the file in and reported text as adopted for a session that was gone. The next session merged against the stale base, so lines deleted since came back — the note property test's rare CI failure (seed 82, then caught with evidence at seed 71, 1 in 900 loaded soak iterations). A stopped document now drops late work: no record (persistRecord, beginWrite), no read-in, no adopted text, no scheduled write, and a write re-checks liveness after its last read (SAFE-A44). The IndexedDB test stand-in now refuses reads and writes after destroy, as the real provider does. Also: the property test prints per-step evidence on failure, and unit-soak's failure excerpt starts at the assertion (ride-along NEC-299). Soak after the fix: 900 idle + 900 loaded, 0 failures.

Spec: docs/changes/NEC-296-deleted-word-came-back-property-test/spec.md

Also fixes NEC-299: unit-soak's failure excerpt is filled by stderr and never reaches the assertion

Changelog

Fixed a rare case where a note reconnected while its first sync was being prepared could act on stale text, which could have undone recent typing awaiting replay in an open editor.

Task: NEC-296 — https://projectron.nerchure.com/tasks/309 A note's document stopped while its seed was being hashed carried on when the hash finished: it stored a stale file record over the next session's, read the file in and reported text as adopted for a session that was gone. The next session merged against the stale base, so lines deleted since came back — the note property test's rare CI failure (seed 82, then caught with evidence at seed 71, 1 in 900 loaded soak iterations). A stopped document now drops late work: no record (persistRecord, beginWrite), no read-in, no adopted text, no scheduled write, and a write re-checks liveness after its last read (SAFE-A44). The IndexedDB test stand-in now refuses reads and writes after destroy, as the real provider does. Also: the property test prints per-step evidence on failure, and unit-soak's failure excerpt starts at the assertion (ride-along NEC-299). Soak after the fix: 900 idle + 900 loaded, 0 failures. Spec: `docs/changes/NEC-296-deleted-word-came-back-property-test/spec.md` Also fixes NEC-299: unit-soak's failure excerpt is filled by stderr and never reaches the assertion ## Changelog Fixed a rare case where a note reconnected while its first sync was being prepared could act on stale text, which could have undone recent typing awaiting replay in an open editor.
Seed 82 failed once on CI with a deleted word back, and never again in
several thousand runs locally or 80 on the runner. A failing sequence now
prints every step's document, file, base, lastSeen, client ids and seed
digests in flight, and the fake store's event log, so the next one can be
read rather than guessed at. Recording takes no turn: all 150 seeds settle
identically with it on and off (client ids pinned).

Task: NEC-296

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHSWLicoRvWzsYjkfdNKYz
Review: the plan asked for the client id at every restart in the trace;
"has not happened again" is about CI history nobody checked, so it reads
"not reproduced"; the two non-deterministic sources are from reading the
code and say so; and the on/off check was against the file before the
change, which the spec now records. Re-checked: 150 seeds identical.

Task: NEC-296

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHSWLicoRvWzsYjkfdNKYz
The excerpt took the first three Error: lines and capped at 120. A test
that logs errors to stderr filled it before the assertion, and the
iteration's own log is lost with the runner: soak run 1483's one failure
in 600 left nothing to read. Now from the first AssertionError, 600
lines; the old grep when there is none.

Task: NEC-296
Also-fixes: NEC-299

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHSWLicoRvWzsYjkfdNKYz
A seed's hash runs in real time on the thread pool. A session stopped
while it was pending carried on when it finished: it seeded its destroyed
document, stored the file's old text as its record over the next
session's, reported that text as adopted, and scheduled a write. The next
session merged the file against the stale base, and lines deleted since
came back — the property test's one-in-hundreds failure on a loaded
runner (NEC-296).

A document no longer in fileDocs now drops a late seed, stores no record
(persistRecord and beginWrite), schedules no disk write, and reports
nothing adopted (SAFE-A44). The test's IndexedDB stand-in refuses reads
and writes after destroy, as the real provider's closed database does.
Each guard was mutated alone and fails a named test.

Task: NEC-296

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHSWLicoRvWzsYjkfdNKYz
Review: a stopped document whose read spanned the stop still read the
file in, telling the view guard, the kept-aside save and the race flag —
all kept by path — about text from a session that was gone. And the
write's liveness check came before its last awaited read, so a stop
landing in it went on to replace the file (compare-and-swap, recorded
first: nothing lost) and counted a refusal against the note.

readIn now returns for a stopped document; writeToDisk checks again
after the read and counts no refusal then. Each mutated alone fails a
named test. The docs say only what the evidence shows.

Task: NEC-296

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHSWLicoRvWzsYjkfdNKYz
Record the soak after the fix, and what it does and does not show
Some checks failed
Release note / release-note (pull_request) Successful in 21s
CI / build (pull_request) Failing after 2m29s
CI / promote (pull_request) Has been cancelled
CI / e2e (pull_request) Has been cancelled
3388531c7b
Task: NEC-296

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHSWLicoRvWzsYjkfdNKYz
cruelacid force-pushed worktree-nec-296-a-word-deleted-from-a-note-came-back-in from 3388531c7b
Some checks failed
Release note / release-note (pull_request) Successful in 21s
CI / build (pull_request) Failing after 2m29s
CI / promote (pull_request) Has been cancelled
CI / e2e (pull_request) Has been cancelled
to 4213bd84f8
Some checks failed
Release note / release-note (pull_request) Successful in 19s
CI / build (pull_request) Successful in 5m31s
CI / e2e (pull_request) Failing after 11m28s
CI / promote (pull_request) Has been skipped
2026-10-17 02:04:43 +01:00
Compare
cruelacid force-pushed worktree-nec-296-a-word-deleted-from-a-note-came-back-in from 4213bd84f8
Some checks failed
Release note / release-note (pull_request) Successful in 19s
CI / build (pull_request) Successful in 5m31s
CI / e2e (pull_request) Failing after 11m28s
CI / promote (pull_request) Has been skipped
to 5058688cbb
All checks were successful
Release note / release-note (pull_request) Successful in 17s
CI / build (pull_request) Successful in 5m29s
CI / e2e (pull_request) Successful in 12m12s
CI / promote (pull_request) Has been skipped
CI / build (push) Successful in 5m34s
CI / e2e (push) Successful in 12m8s
CI / promote (push) Successful in 30s
2026-10-17 07:44:45 +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!302
No description provided.