fix: memoryFlush never triggers when compactionCount and memoryFlushCompactionCount are both 0#47247
Conversation
|
Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit. |
Greptile SummaryThis PR patches the Key changes
Notable concern Confidence Score: 2/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/auto-reply/reply/memory-flush.ts
Line: 231-236
Comment:
**Fix breaks flush guard for cycle-0 flushes without compaction**
The special-case `false` for the `0/0` pair conflates two genuinely different states:
1. **Initial state** – `compactionCount: 0`, `memoryFlushCompactionCount: undefined` (a flush has never occurred; correctly returns `false` via the earlier `typeof` check).
2. **Post-flush at cycle 0** – `compactionCount: 0`, `memoryFlushCompactionCount: 0` (a flush already ran but no compaction occurred, so `incrementCompactionCount` was never called and both counters stayed at 0).
In `agent-runner-memory.ts` (lines 523–526), `memoryFlushCompactionCount` is initialised to the _current_ `compactionCount` (defaulting to `0`) and only changes when `memoryCompactionCompleted = true`. If a flush completes without emitting a compaction-end event, it writes `memoryFlushCompactionCount: 0` back to storage. The new code then returns `false` on every subsequent call, meaning the flush guard is permanently bypassed for any session where `compactionCount` remains `0`. Every time the token threshold is hit the flush will run again, defeating the idempotency guarantee the function was designed to provide.
The existing `undefined`-check already handles the "never flushed" path cleanly. A safer fix would be to use a sentinel (e.g. `-1`) to represent "flushed at cycle 0, no compaction yet" vs. leaving `memoryFlushCompactionCount` `undefined` until the first real flush, rather than overriding the equality check at `0/0`:
```ts
// In agent-runner-memory.ts, store -1 to mean
// "flushed before first compaction"
let memoryFlushCompactionCount =
(activeSessionEntry?.compactionCount ?? 0) === 0 && !memoryCompactionCompleted
? -1
: activeSessionEntry?.compactionCount ?? 0;
```
And then in `hasAlreadyFlushedForCurrentCompaction`:
```ts
// -1 sentinel = flushed at pre-compaction cycle; treat as "already flushed" only while compactionCount is still 0
if (lastFlushAt === -1) {
return compactionCount === 0;
}
return lastFlushAt === compactionCount;
```
This makes the distinction explicit and restores the idempotency guard for the cycle-0 case.
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: eea7873 |
| // If both are 0, this is the initial state (never flushed for real). | ||
| // This handles the case where memoryFlushCompactionCount was set to 0 | ||
| // during the first flush but compactionCount hasn't changed yet. | ||
| if (lastFlushAt === 0 && compactionCount === 0) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Fix breaks flush guard for cycle-0 flushes without compaction
The special-case false for the 0/0 pair conflates two genuinely different states:
- Initial state –
compactionCount: 0,memoryFlushCompactionCount: undefined(a flush has never occurred; correctly returnsfalsevia the earliertypeofcheck). - Post-flush at cycle 0 –
compactionCount: 0,memoryFlushCompactionCount: 0(a flush already ran but no compaction occurred, soincrementCompactionCountwas never called and both counters stayed at 0).
In agent-runner-memory.ts (lines 523–526), memoryFlushCompactionCount is initialised to the current compactionCount (defaulting to 0) and only changes when memoryCompactionCompleted = true. If a flush completes without emitting a compaction-end event, it writes memoryFlushCompactionCount: 0 back to storage. The new code then returns false on every subsequent call, meaning the flush guard is permanently bypassed for any session where compactionCount remains 0. Every time the token threshold is hit the flush will run again, defeating the idempotency guarantee the function was designed to provide.
The existing undefined-check already handles the "never flushed" path cleanly. A safer fix would be to use a sentinel (e.g. -1) to represent "flushed at cycle 0, no compaction yet" vs. leaving memoryFlushCompactionCount undefined until the first real flush, rather than overriding the equality check at 0/0:
// In agent-runner-memory.ts, store -1 to mean
// "flushed before first compaction"
let memoryFlushCompactionCount =
(activeSessionEntry?.compactionCount ?? 0) === 0 && !memoryCompactionCompleted
? -1
: activeSessionEntry?.compactionCount ?? 0;And then in hasAlreadyFlushedForCurrentCompaction:
// -1 sentinel = flushed at pre-compaction cycle; treat as "already flushed" only while compactionCount is still 0
if (lastFlushAt === -1) {
return compactionCount === 0;
}
return lastFlushAt === compactionCount;This makes the distinction explicit and restores the idempotency guard for the cycle-0 case.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/memory-flush.ts
Line: 231-236
Comment:
**Fix breaks flush guard for cycle-0 flushes without compaction**
The special-case `false` for the `0/0` pair conflates two genuinely different states:
1. **Initial state** – `compactionCount: 0`, `memoryFlushCompactionCount: undefined` (a flush has never occurred; correctly returns `false` via the earlier `typeof` check).
2. **Post-flush at cycle 0** – `compactionCount: 0`, `memoryFlushCompactionCount: 0` (a flush already ran but no compaction occurred, so `incrementCompactionCount` was never called and both counters stayed at 0).
In `agent-runner-memory.ts` (lines 523–526), `memoryFlushCompactionCount` is initialised to the _current_ `compactionCount` (defaulting to `0`) and only changes when `memoryCompactionCompleted = true`. If a flush completes without emitting a compaction-end event, it writes `memoryFlushCompactionCount: 0` back to storage. The new code then returns `false` on every subsequent call, meaning the flush guard is permanently bypassed for any session where `compactionCount` remains `0`. Every time the token threshold is hit the flush will run again, defeating the idempotency guarantee the function was designed to provide.
The existing `undefined`-check already handles the "never flushed" path cleanly. A safer fix would be to use a sentinel (e.g. `-1`) to represent "flushed at cycle 0, no compaction yet" vs. leaving `memoryFlushCompactionCount` `undefined` until the first real flush, rather than overriding the equality check at `0/0`:
```ts
// In agent-runner-memory.ts, store -1 to mean
// "flushed before first compaction"
let memoryFlushCompactionCount =
(activeSessionEntry?.compactionCount ?? 0) === 0 && !memoryCompactionCompleted
? -1
: activeSessionEntry?.compactionCount ?? 0;
```
And then in `hasAlreadyFlushedForCurrentCompaction`:
```ts
// -1 sentinel = flushed at pre-compaction cycle; treat as "already flushed" only while compactionCount is still 0
if (lastFlushAt === -1) {
return compactionCount === 0;
}
return lastFlushAt === compactionCount;
```
This makes the distinction explicit and restores the idempotency guard for the cycle-0 case.
How can I resolve this? If you propose a fix, please make it concise.
Fixes #47143
Problem
In
hasAlreadyFlushedForCurrentCompaction, when a session has:compactionCount: 0(never compacted)memoryFlushCompactionCount: 0(never flushed)The function returned
0 === 0 → true, incorrectly indicating "already flushed", so the memory flush was skipped.Solution
Added a special case to return
falsewhen bothcompactionCountandmemoryFlushCompactionCountare 0. This treats the 0/0 case as the initial state (never flushed) rather than "already flushed at count 0".Changes
Modified
hasAlreadyFlushedForCurrentCompactioninmemory-flush.ts:undefinedmemoryFlushCompactionCountfalsewhen both counts are 0Updated tests in
reply-state.test.ts:falsecompactionCountwithmemoryFlushCompactionCount: 0memoryFlushCompactionCount: 0withcompactionCount: 1Testing
All related tests pass:
hasAlreadyFlushedForCurrentCompactionsuitememory-flush.test.ts