Skip to content

Guard the condense swap against memory appended during the summary - #79

Merged
aaroncoville merged 1 commit into
theme/sixth-historyfrom
fix/condense-swap-guard
Nov 12, 2026
Merged

aaroncoville merged 1 commit into
theme/sixth-historyfrom
fix/condense-swap-guard

Conversation

@aaroncoville

Copy link
Copy Markdown
Owner

What

Condensing could lose memory appended while the summary call was running: the rebuilt file was renamed over the original without checking whether the original had changed.

Ported from upstream HarnessMD#842 (commits 9a560db test, 6143a40 fix, ef78130 tidy), squashed into one commit. One conflict was resolved: this fork's not-smaller back-off in the autonomous loop is kept, and only the read changes to raw bytes.

Fix

  • Re-read memory.md as raw bytes just before the rename. An append is carried over verbatim (up to 3 re-check rounds).
  • Any other change aborts with source-changed, a missing or unreadable file aborts with source-check-failed, and a non-UTF-8 source is refused before the LLM call.
  • The temp file is always removed on failure. A successful swap logs carriedBytes.

Evidence

Before

$ node --test test/reflect-source-change.test.cjs   # on the fork base, upstream test only
not ok 2 - a write appended while the summarizer is pending is carried onto the condensed file
... (14 failing)
# pass 3  # fail 14

After

$ node --test test/reflect-source-change.test.cjs
# pass 17  # fail 0
$ npm run test:focused
# tests 2560  # pass 2557  # fail 0  # skipped 3

Typecheck is clean. Tested on macOS. No UI change.

Condensing reads memory.md, waits on a summary call that can take minutes,
then renames the rebuilt file over the original. Anything an agent appended
in between was overwritten by the rename and lost.

The swap now re-reads memory.md as raw bytes just before the rename and
compares it with the snapshot the summary was built from:
- bytes appended after the snapshot are carried onto the new file verbatim,
  re-checking up to three rounds while the file keeps growing;
- any other change aborts with source-changed (the next pass retries from
  the new file), and a missing or unreadable file aborts with
  source-check-failed;
- a source that is not valid UTF-8 is refused before the summary call
  (source-encoding-invalid), since a lossy decode would corrupt it;
- the temp file is removed on every failure path, and a successful swap logs
  carriedBytes.

The not-smaller back-off on the autonomous loop is unchanged.

Ported from upstream PR HarnessMD#842 (commits 9a560db,
6143a40, ef78130).
@aaroncoville aaroncoville added the no-visual-change Maintainer waiver for the before/after evidence check: nothing visual to show label Nov 12, 2026
@aaroncoville
aaroncoville merged commit cad4657 into theme/sixth-history Nov 12, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-visual-change Maintainer waiver for the before/after evidence check: nothing visual to show

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant