fix(memory): memoryFlush fires every compaction cycle instead of every other#51421
Conversation
Greptile SummaryThis PR fixes a subtle off-by-one bug where How the bug worked: After a successful flush + compaction, Why the fix is correct: By not reassigning
Key observations:
Confidence Score: 5/5
Last reviewed commit: "fix(memory): memoryF..." |
|
Updated with improvements from prior PRs:
Thanks to @lailoo, @Br1an67, and @dial481 for keeping this issue alive and contributing fixes. This PR combines the cleanest elements from the prior attempts. |
56d0810 to
d19afe3
Compare
|
Rebased on main (v2026.3.22). Conflict in |
d19afe3 to
3aeb747
Compare
|
Branch rebased onto latest main, resolving merge conflicts in Requesting re-review. |
3aeb747 to
0782639
Compare
|
Local test note: |
3504628 to
c7c1d3c
Compare
|
Rebased onto current No code changes — same 2 commits, same 2 files. CI was red before due to stale base (1,597 commits behind); builds are now passing. Note: @codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Ready for review. Summary:
Closes #12590. |
c7c1d3c to
80742fb
Compare
|
Rebased onto current No conflicts — the buggy reassignment at Also commented on #12590 with current-state findings. @codex review |
|
Codex Review: Didn't find any major issues. 🚀 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
80742fb to
9732faa
Compare
|
Rebased onto current Re-verified the bug is still present in the current main / v2026.4.15. The structure was refactored (local-import → Regression test ( @codex review |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Related work from PRtags group Title: Open PR duplicate: memoryFlush compaction counter skip
|
|
ProjectClownfish pushed a narrow repair to this branch so the original contributor path can stay canonical. Source PR: #51421 |
9732faa to
21281a2
Compare
b27aeef to
b744861
Compare
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
b744861 to
855852e
Compare
855852e to
6c52138
Compare
6c52138 to
4ffe486
Compare
4ffe486 to
5018391
Compare
|
This assigned pull request has been automatically marked as stale after being open for 27 days. |
5ff9131 to
f94f9c2
Compare
|
Landed via rebase onto main.
Thanks @Kaspre! |
Summary
memoryFlushCompactionCountto the post-increment valueRoot cause
After a memoryFlush during compaction,
memoryFlushCompactionCountis reassigned to the post-incrementcompactionCount(line 536 ofagent-runner-memory.ts). This locks both counters to the same value. On the next cycle, the dedup gate atreply-state.tsseesmemoryFlushCompactionCount === compactionCountand skips the flush. Only after a regular compaction (without flush) incrementscompactionCountalone do the values differ again, allowing the next flush.The result: flush, skip, flush, skip — memoryFlush fires on every other compaction instead of every one.
Fix
Remove the reassignment.
memoryFlushCompactionCountstays at the pre-increment value (N). AfterincrementCompactionCount(),compactionCountbecomes N+1. Next cycle,N !== N+1, flush fires. The counter is then updated through the normal session store update path.Prior art
This issue has been reported and patch-attempted multiple times:
We've been running this fix as a local dist patch since early March 2026 with no issues.
Fixes #12590.
Real behavior proof
Behavior or issue addressed: memoryFlush silently skips every other auto-compaction ([Bug]:
memoryFlushdoes not fire reliably #12590). After flush+compaction,memoryFlushCompactionCountis reassigned to the post-incrementcompactionCount, which makes the next cycle's dedup checkmemoryFlushCompactionCount === compactionCounttrue → flush is skipped → on the cycle after, the counter has drifted again → flush fires. Result: ~50% miss rate on durable note writes.Real environment tested: Local OpenClaw v2026.5.6 install on Linux 6.6.87.2 / WSL2 / Node v25.8.2, gateway running as systemd user service (pid 270001, active). Patch has been carried across v2026.3.8 → v2026.4.15 → v2026.4.24 → v2026.5.6 since 2026-03-08 with no regressions in any version.
Exact steps or command run after this patch: applied this PR's one-line change (drop the
if (typeof nextCount === "number") { memoryFlushCompactionCount = nextCount; }block indist/agent-runner.runtime-Ew7ojxcl.js, line 2065 of the v2026.5.6 bundle), restarted the gateway (systemctl --user restart openclaw-gateway), and verified by inspecting both the source-level fault and the patched bundle.Evidence after fix:
Before patch — bug still live in v2026.5.6 source (
src/auto-reply/reply/agent-runner-memory.ts:979-981):After patch — this PR's change applied to the v2026.5.6 bundle on my install:
Observed result after fix: With the reassignment removed,
memoryFlushCompactionCountstays at its pre-increment value whileincrementCompactionCountadvancescompactionCountto N+1. The next compaction cycle's dedup check seesN !== N+1and the flush fires instead of being skipped. The gateway has run continuously across four version upgrades with this patch carried forward, including session reloads, manual/restartcycles, and multi-day-long sessions; no regressions or new errors in the gateway log have surfaced that trace back to this change.What was not tested: I have not constructed an end-to-end repro that forces ≥ 3 consecutive auto-compactions in one synthetic test session and asserts memoryFlush firing on every cycle (rather than alternating). The compaction trigger is token-threshold-based and would require either a very large prompt loop or a test seam that lets you bypass the threshold. The pre-existing
reply-state.test.tscoverage ofmemoryFlushCompactionCountis left to vouch for that behavior at the unit level.Test plan
reply-state.test.tstests formemoryFlushCompactionCountlogic pass🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com