You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
syncAllPeers() (server/services/syncOrchestrator.js:829-877) compacts the brain sync log only inside if (brainPeers.length > 0) (:870-876). On an install with no enabled brain-sync peer — the default, since federation is opt-in — compactLog is never called, so every create / update / upsertWithId / remove on all ten brain types (brainStorage.js:458, 491, 540, 592, 638, 684, 725) appends a full-record entry to data/brain/sync_log.jsonl that nothing will ever read.
The guard was added in 5217deb (#1077) to avoid Math.min() over an empty array (Infinity), not as a decision to keep an unread log forever — the comment beside it is about a reporting-less peer floor of 0, which is a different case.
Trigger
Any single-machine install writing brain records. The heaviest producer is the Daily Log: DailyLogTab.jsx:20-21 autosaves on a 1.5 s debounce / 10 s max-wait, and each PUT → setJournalContent → upsertWithId appends the day's entirecontentandsegments (which duplicate the text, brainJournal.js:361-366) as one log line. An hour of journaling adds hundreds of multi-KB entries; a ChatGPT import adds ~1,400 entries of up to 9.8 KB each. None of it is ever pulled.
Impact
sync_log.jsonl grows without bound and lives under data/, so it rides every backup.
initSyncLog() (server/services/brainSyncLog.js:27-46) readFiles the whole file into one string and splits it just to read the last line — boot latency and a 2–3× file-size memory spike scale with the user's entire brain edit history.
In syncAllPeers, add an else branch to the brainPeers.length > 0 block:
}elseif(brainSyncLog.getCurrentSeq()>0){// No brain-sync peer will ever pull these entries; a peer added later// converges through the reconcile snapshot (#1077). Keep the last entry so// initSyncLog still recovers the sequence counter across restarts.awaitbrainSyncLog.compactLog(brainSyncLog.getCurrentSeq());}
compactLog(minSeq) keeps entries with seq >= minSeq (brainSyncLog.js:147), so the newest entry survives and the counter never resets to 0 — which matters because a peer that was removed and later re-added still holds a cursorForYou.brainSeq > 0 and would otherwise skip new entries until reconcile caught it. The > 0 guard keeps a fresh install from rewriting an empty file every minute. compactLog already logs only when it runs; it is a no-op rewrite when nothing is below the floor, so add a cheap dropped === 0 early return inside compactLog (or check lines.length > 1 before rewriting) so an idle install doesn't rewrite the file every 60 s.
Rejected: skipping the appendChange entirely when there are no peers — changes the write contract at seven call sites and leaves a peer added later with no delta log at all. Rejected:compactLog(Infinity) / truncating to empty — resets the sequence counter on the next boot (see above).
Tests: in syncOrchestrator.test.js next to "floors at 0 when a brain-enabled peer has not reported its cursor into us" (~:838): (a) with peers configured but none brain-enabled, compactLog is called with getCurrentSeq(); (b) with getCurrentSeq() === 0, compactLog is not called; (c) the existing two floor tests stay green. In brainSyncLog.test.js: compactLog(currentSeq) keeps exactly the last line and a subsequent initSyncLog recovers that seq.
Acceptance criteria
On an install with no enabled brain-sync peer, sync_log.jsonl never holds more than the newest entry after a sync cycle.
After that compaction and a restart, getCurrentSeq() equals the pre-restart value (no reset to 0).
An idle install (no new entries since the last cycle) does not rewrite the file every cycle.
Existing peer-floor behavior (min over cursorForYou.brainSeq, 0 for an unreported peer) is unchanged; cd server && npm test green.
Problem
syncAllPeers()(server/services/syncOrchestrator.js:829-877) compacts the brain sync log only insideif (brainPeers.length > 0)(:870-876). On an install with no enabled brain-sync peer — the default, since federation is opt-in —compactLogis never called, so everycreate/update/upsertWithId/removeon all ten brain types (brainStorage.js:458, 491, 540, 592, 638, 684, 725) appends a full-record entry todata/brain/sync_log.jsonlthat nothing will ever read.The guard was added in 5217deb (#1077) to avoid
Math.min()over an empty array (Infinity), not as a decision to keep an unread log forever — the comment beside it is about a reporting-less peer floor of 0, which is a different case.Trigger
Any single-machine install writing brain records. The heaviest producer is the Daily Log:
DailyLogTab.jsx:20-21autosaves on a 1.5 s debounce / 10 s max-wait, and each PUT →setJournalContent→upsertWithIdappends the day's entirecontentandsegments(which duplicate the text,brainJournal.js:361-366) as one log line. An hour of journaling adds hundreds of multi-KB entries; a ChatGPT import adds ~1,400 entries of up to 9.8 KB each. None of it is ever pulled.Impact
sync_log.jsonlgrows without bound and lives underdata/, so it rides every backup.initSyncLog()(server/services/brainSyncLog.js:27-46)readFiles the whole file into one string andsplits it just to read the last line — boot latency and a 2–3× file-size memory spike scale with the user's entire brain edit history.syncOrchestrator.js:172), each page re-parsing the full file (see the companion read-path issue), even though the Brain sync is delta-log-only — diverged peers never re-converge (no anti-entropy) #1077 anti-entropy snapshot would converge it in one exchange.Fix
In
syncAllPeers, add anelsebranch to thebrainPeers.length > 0block:compactLog(minSeq)keeps entries withseq >= minSeq(brainSyncLog.js:147), so the newest entry survives and the counter never resets to 0 — which matters because a peer that was removed and later re-added still holds acursorForYou.brainSeq > 0and would otherwise skip new entries until reconcile caught it. The> 0guard keeps a fresh install from rewriting an empty file every minute.compactLogalready logs only when it runs; it is a no-op rewrite when nothing is below the floor, so add a cheapdropped === 0early return insidecompactLog(or checklines.length > 1before rewriting) so an idle install doesn't rewrite the file every 60 s.Rejected: skipping the
appendChangeentirely when there are no peers — changes the write contract at seven call sites and leaves a peer added later with no delta log at all. Rejected:compactLog(Infinity)/ truncating to empty — resets the sequence counter on the next boot (see above).Files:
server/services/syncOrchestrator.js(~5 lines),server/services/brainSyncLog.js(no-op guard),server/services/syncOrchestrator.test.js,server/services/brainSyncLog.test.js.Tests: in
syncOrchestrator.test.jsnext to "floors at 0 when a brain-enabled peer has not reported its cursor into us" (~:838): (a) with peers configured but none brain-enabled,compactLogis called withgetCurrentSeq(); (b) withgetCurrentSeq() === 0,compactLogis not called; (c) the existing two floor tests stay green. InbrainSyncLog.test.js:compactLog(currentSeq)keeps exactly the last line and a subsequentinitSyncLogrecovers that seq.Acceptance criteria
sync_log.jsonlnever holds more than the newest entry after a sync cycle.getCurrentSeq()equals the pre-restart value (no reset to 0).cursorForYou.brainSeq, 0 for an unreported peer) is unchanged;cd server && npm testgreen.