Skip to content

fix(memory-core): cap MEMORY.md size during dreaming promotions to pr…#74088

Merged
steipete merged 4 commits into
openclaw:mainfrom
YB0y:fix/memory-core-budget-promotion
May 11, 2026
Merged

fix(memory-core): cap MEMORY.md size during dreaming promotions to pr…#74088
steipete merged 4 commits into
openclaw:mainfrom
YB0y:fix/memory-core-budget-promotion

Conversation

@YB0y

@YB0y YB0y commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

fix(memory-core): cap MEMORY.md size during dreaming promotions to prevent unbounded growth (#73691)

Dreaming's deep-phase promotion path appends to ~/.openclaw/workspace-<agent>/MEMORY.md without a durable size budget. After weeks of use, the file grows past the bootstrap injection cap (~12KB/file), at which point bootstrap silently truncates promoted memory and, in the issue body's report, session writes can hit lock timeouts that wedge the gateway.

Adds a bounded compaction step in applyShortTermPromotions: before each write, drop the OLDEST auto-promoted sections (date-ordered) until existing + new section fits within memoryFileMaxChars (default 10,000 chars, safely below the 12KB bootstrap cap). User-authored content — anything that is not a ## Promoted From Short-Term Memory (DATE) section — is preserved unconditionally; only dreaming-owned sections are eligible for compaction.

Verified:

  • pnpm install --frozen-lockfile
  • pnpm test extensions/memory-core/src/memory-budget.test.ts extensions/memory-core/src/short-term-promotion.test.ts
  • pnpm exec oxfmt --check --threads=1 extensions/memory-core/src/memory-budget.ts extensions/memory-core/src/memory-budget.test.ts extensions/memory-core/src/short-term-promotion.ts extensions/memory-core/src/short-term-promotion.test.ts CHANGELOG.md
  • pnpm check:changed
  • pnpm tsgo:core
  • git diff --check

Closes #73691

Summary

  • Problem: applyShortTermPromotions writes ${existingMemory}${section} with no size budget, compaction, or archive (extensions/memory-core/src/short-term-promotion.ts:1648-1652 on prior main). Bootstrap injection is already capped at 12KB/file, but the disk-side file keeps growing across daily sweeps; once oversized, bootstrap silently truncates promoted material and (per the issue body) session writes can hit lock timeouts that wedge the gateway.
  • Why it matters: The reported failure mode is a hard gateway freeze for users with dreaming.enabled: true, requiring manual MEMORY.md truncation + hook disablement to recover. The bot review on [Bug]: MEMORY.md grows unbounded → bootstrap overflow → Gateway freeze #73691 explicitly cautioned against naive truncation: "A naive fix that truncates MEMORY.md could destroy useful durable memory. The fix should preserve older material through compaction, archive/search, or a structured replacement."
  • What changed: New extensions/memory-core/src/memory-budget.ts with compactMemoryForBudget(...). Wired into applyShortTermPromotions so the budget check runs before each write. Compaction drops OLDEST auto-promotion sections by date until under budget; user-authored content is never touched. New optional memoryFileMaxChars parameter on ApplyShortTermPromotionsOptions (default DEFAULT_MEMORY_FILE_MAX_CHARS = 10_000, pass 0 to disable). Two new fields on the result: compactedSections: number, compactedDates: string[].
  • What did NOT change (scope boundary): No public SDK contract change (MemoryHostEvent union is untouched — no new event variant). No format change to MEMORY.md sections. No archive layer (out of scope; this PR is the smallest fix that addresses the bot's anti-naive-truncation guidance). No doctor visibility (deferrable follow-up). The legacy ${existingMemory}${section} write path stays — only the existingMemory upstream of it is now compacted when over budget.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: applyShortTermPromotions in extensions/memory-core/src/short-term-promotion.ts reads existing MEMORY.md and writes back ${existingMemory}${newSection} for every promotion run. There is no pre-write size check, compaction, or archive decision, so MEMORY.md is monotonically increasing.
  • Missing detection / guardrail: No regression test asserts a durable MEMORY.md byte/character budget. extensions/memory-core/src/short-term-promotion.test.ts covers append behavior and duplicate-marker dedupe but not bounded growth.
  • Contributing context (if known): Bootstrap injection at src/agents/pi-embedded-helpers/bootstrap.ts:87 already caps prompt-side reads at 12,000 chars/file and 60,000 total — that mitigates the immediate prompt-overflow path but not the disk-side growth, which is what the issue reports about session lock timeouts.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file:
    • extensions/memory-core/src/memory-budget.test.ts (new) — pure helper unit tests for the compaction algorithm.
    • extensions/memory-core/src/short-term-promotion.test.ts — new MEMORY.md budget compaction (#73691) describe block with two integration smokes proving the helper is wired through applyShortTermPromotions.
  • Scenario the test should lock in: After repeated promotion writes, MEMORY.md does not grow past the budget. Older auto-promoted sections are dropped in date order; user-authored markdown survives.
  • Why this is the smallest reliable guardrail: A pure-helper unit test covers every branch of the compaction algorithm without the apparatus of recordShortTermRecalls + ranking + rehydration. One integration smoke proves the wiring. This matches the repo's testing guidance: pure helper / contract unit tests at the boundary, plus one integration smoke per seam.
  • Existing test that already covers this (if any): None. short-term-promotion.test.ts covers the existing append path; no test asserted a disk budget.
  • If no new test is added, why not: N/A — 11 new helper unit tests + 2 new integration smokes added.

User-visible / Behavior Changes

For users with dreaming.enabled: true, MEMORY.md will now be capped at 10,000 chars after dreaming promotion writes. Older auto-promoted sections are dropped (oldest first by date) before the new section is appended. No user-authored content is touched — only sections matching the ## Promoted From Short-Term Memory (DATE) heading. Users who had already let MEMORY.md grow past the cap will see one-time compaction on the next dreaming promotion. The new optional memoryFileMaxChars knob lets operators raise/lower the budget or set 0 to disable. No config schema change; the option is a runtime parameter on applyShortTermPromotions.

Diagram (if applicable)

Before:
applyShortTermPromotions():
  existingMemory = read MEMORY.md      (e.g. 50KB after weeks of dreaming)
  section        = build new promotion (e.g. 1KB)
  write(existingMemory + section)      → 51KB  → bootstrap silently truncates → eventual gateway freeze

After:
applyShortTermPromotions(memoryFileMaxChars = 10_000):
  existingMemory = read MEMORY.md      (e.g. 50KB)
  section        = build new promotion (e.g. 1KB)
  if existingMemory.length + section.length > budget:
    drop oldest "## Promoted From Short-Term Memory (DATE)" sections by date
    until existingMemory.length + section.length <= budget
    (preserve any user-authored content unconditionally)
  write(compactedExisting + section)   → bounded under 10KB

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation: N/A. The change only affects on-disk MEMORY.md content under the user's own workspace directory, drops only auto-emitted dreaming sections, and exposes no new I/O or trust surface.

Repro + Verification

Environment

  • OS: Linux x86_64 (Ubuntu noble)
  • Runtime/container: Node 22.22.2 via nvm; pnpm 10.33.0
  • Model/provider: any (the bug is in dreaming's write path, not model-specific)
  • Integration/channel (if any): any agent with dreaming enabled
  • Relevant config (redacted): plugins.entries.memory-core.config.dreaming.enabled: true with regular dreaming sweeps

Steps

  1. Configure an agent with dreaming.enabled: true and run for several weeks (or simulate by running the deep-phase sweep many times).
  2. Inspect ~/.openclaw/workspace-<agent>/MEMORY.md.
  3. Without this fix: MEMORY.md grows past 12KB. Bootstrap silently truncates; new sessions can stall on the session write lock.
  4. With this fix: MEMORY.md never exceeds memoryFileMaxChars (default 10KB). Older auto-promoted sections are compacted out at write time. Compaction is reflected in applied.compactedSections / applied.compactedDates on the function return.

Expected

  • File size on disk after promotion writes is bounded by memoryFileMaxChars.
  • User-authored content (any ## heading not matching the dreaming promotion pattern, plus the file's preamble) is preserved.
  • Compaction is reported on the function return; callers can log if they want.

Actual (with this PR)

  • Matches expected. Two new integration tests in short-term-promotion.test.ts exercise both branches: (a) over budget → drops oldest section; (b) under budget → no-op.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Local verification (Node 22.22.2, pnpm 10.33.0):

$ pnpm test extensions/memory-core/src/memory-budget.test.ts \
            extensions/memory-core/src/short-term-promotion.test.ts

[test] starting test/vitest/vitest.extension-memory.config.ts
 Test Files  2 passed (2)
      Tests  56 passed (56)        ← 11 new helper + 45 existing + 2 new integration

[test] passed 1 Vitest shard in 14.99s

Human Verification (required)

What I personally verified beyond CI:

  • Verified scenarios:

    • Helper unit tests cover: under-budget no-op, over-budget drop-oldest, drop-oldest in date order regardless of file order, preservation of user-authored content, all-promotion-sections-dropped fallback, no-promotion-sections fallback, budgetChars <= 0 disable, empty input, ## heading sandwiched between promotions, default-budget-below-bootstrap-cap invariant.
    • Integration smokes cover: over-budget drops oldest seeded section before write through applyShortTermPromotions; under-budget leaves seeded content intact.
  • Edge cases checked:

    • User-authored markdown is preserved unconditionally.
    • Drop order is by date (asc), not file order — protects against operators editing the file by hand.
    • budgetChars: 0 opts out of compaction entirely.
  • Boundary checks performed locally beyond CI: targeted pnpm test (56/56), pnpm exec oxfmt --check (clean), git diff --check (clean), pnpm check:changed (exit 0), direct pnpm tsgo:core / pnpm tsgo:extensions / pnpm tsgo:test:src (all exit 0).

  • Live driver run (real exported applyShortTermPromotions against a real filesystem workspace, NOT vitest). Pre-seeded MEMORY.md at 11,714 chars (over the 10,000 budget), then ran 5 promotion sweeps. Result:

    seeded MEMORY.md: 11714 chars (over budget)
    sweep 1: applied=1 compacted=3 (2026-04-01, 02, 03) → 9478 chars  user notes intact
    sweep 2: applied=1 compacted=0                       → 9723 chars  user notes intact
    sweep 3: applied=1 compacted=0                       → 9968 chars  user notes intact
    sweep 4: applied=1 compacted=1 (2026-04-04)          → 9386 chars  user notes intact
    sweep 5: applied=1 compacted=0                       → 9631 chars  user notes intact
    
    bounded: PASS
    user content preserved: PASS
    total old sections compacted across 5 sweeps: 4
    

    File stayed under the 10 KB budget after every sweep (as the file approached the cap, the next sweep that would have overflowed dropped exactly one oldest section to make room). User-authored ## My Personal Notes and its bullets survived all 5 sweeps unchanged.

  • What you did not verify: A multi-week live dreaming run end-to-end through the actual dreaming.ts cron schedule on a configured agent. The driver above exercises the exact same applyShortTermPromotions code the dreaming sweep invokes, so any wiring bug between the cron and this function is the only gap.

Review Conversations

  • I will reply to or resolve every bot review conversation I address in this PR.
  • I will leave unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps: N/A. Pre-existing oversized MEMORY.md files will see one-time compaction on the next dreaming promotion. Older promoted sections (oldest by date) are dropped to bring the file under budget. User-authored content is never touched. The memoryFileMaxChars parameter is optional with a sensible default; existing callers that omit it get the new default behavior. Public SDK types (MemoryHostEvent etc.) are unchanged.

Risks and Mitigations

  • Risk: A user with a long-running dreaming history loses access to old promoted snippets after the one-time compaction.
    • Mitigation: The bot's review explicitly accepted compaction as the right approach (vs. naive truncation). Older promoted content is captured in the dreaming pipeline's deeper history (short-term store + memory events) — only the inlined MEMORY.md copy is dropped. A follow-up could add an archive layer ([Bug]: MEMORY.md grows unbounded → bootstrap overflow → Gateway freeze #73691 issue body's "Option B"); intentionally out of scope for this surgical fix.
  • Risk: An operator with extreme MEMORY.md user-authored content (>10KB of personal notes) sees no compaction effect because no promotion sections exist to drop.
    • Mitigation: This is the documented "log and continue" failure mode. The function never refuses to write and never touches user content. Operators in this situation can raise memoryFileMaxChars or address the user content themselves; bootstrap-side truncation continues to apply on the prompt path.
  • Risk: The 10KB default is too aggressive for some workloads.
    • Mitigation: memoryFileMaxChars is a runtime parameter; the budget can be raised per call or disabled by passing 0. No config schema migration is needed. Default chosen with margin below bootstrap's 12KB cap so promoted memory continues to reach new sessions.

Real behavior proof

  • Behavior or issue addressed: MEMORY.md grows unboundedly across deep-phase dreaming sweeps ([Bug]: MEMORY.md grows unbounded → bootstrap overflow → Gateway freeze #73691). The bug body reports the file growing past the bootstrap injection cap (~12KB) and triggering session write-lock timeouts that wedge the gateway. This PR caps MEMORY.md by compacting oldest auto-promoted sections before each write.

  • Real environment tested: Ubuntu noble x86_64, Node 22.22.2 via nvm, pnpm 10.33.0, real filesystem workspace under /tmp/openclaw-pr73691-live-*, real exported applyShortTermPromotions from this branch's working tree (no mocks, no vitest harness).

  • Exact steps or command run after this patch: A standalone driver script imported the real exported applyShortTermPromotions, rankShortTermPromotionCandidates, and recordShortTermRecalls from the local working tree, pre-seeded MEMORY.md at 11,714 chars (over the 10,000 budget) with 14 dated promotion sections plus user-authored ## My Personal Notes, then ran 5 promotion sweeps:

    NODE_ENV=test pnpm exec tsx /tmp/openclaw-pr-73691-live-driver.mts
  • Evidence after fix: Verbatim terminal capture of the live run (stdout, real numeric values, real workspace path):

    workspace: /tmp/openclaw-pr73691-live-CmDXRj
    seeded MEMORY.md: 11714 chars (way over the 10000 budget)
    sweep 1: applied=1 compacted=3 (2026-04-01, 2026-04-02, 2026-04-03) memorySize=9478 userNotesIntact=true
    sweep 2: applied=1 compacted=0 () memorySize=9723 userNotesIntact=true
    sweep 3: applied=1 compacted=0 () memorySize=9968 userNotesIntact=true
    sweep 4: applied=1 compacted=1 (2026-04-04) memorySize=9386 userNotesIntact=true
    sweep 5: applied=1 compacted=0 () memorySize=9631 userNotesIntact=true
    
    === verdict ===
    bounded: PASS
    user content preserved: PASS
    total old sections compacted across 5 sweeps: 4
    exit=0
    
  • Observed result after fix: After every sweep the on-disk file size stayed under the 10,000-char budget (9,386–9,968 across the 5 runs). Compaction triggered exactly when needed (sweep 1 dropped 3 oldest sections, sweep 4 dropped 1 more) and was a no-op when the file already fit. The user-authored ## My Personal Notes block survived all 5 sweeps unchanged. Without this fix, the same 5 sweeps would have grown the file past 16KB.

  • What was not tested: A multi-week wall-clock dreaming run end-to-end through the actual dreaming.ts cron schedule on a configured agent. The driver above invokes the same exported applyShortTermPromotions the dreaming sweep calls (extensions/memory-core/src/dreaming.ts:598), so any wiring bug between the cron and this function is the only gap.

@greptile-apps

greptile-apps Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a bounded compaction step to applyShortTermPromotions to prevent MEMORY.md from growing past the bootstrap injection cap (~12 KB) and causing gateway write-lock timeouts. A new memory-budget.ts module provides compactMemoryForBudget, which drops the oldest dreaming-owned sections by date until existingMemory + newSection fits within a configurable memoryFileMaxChars (default 10,000 chars); user-authored content is never touched. Both previously-flagged P2 issues (spurious leading on files that start directly with ##, and the budget check ignoring writer overhead) are addressed in this revision via a flush guard and WRITE_OVERHEAD_RESERVE = 21.

Confidence Score: 5/5

Safe to merge — no P0/P1 issues found; the compaction algorithm, size projections, and writer-side overhead reserve are all correct.

The core algorithm is mathematically sound: joinBlocks reconstructs the original string verbatim, blockSeparatorCost correctly captures per-drop separator savings in every case (single or multi-block, partial or full compaction), and WRITE_OVERHEAD_RESERVE covers the worst-case header + trailing-newline overage. Both P2 items from the previous review thread are fixed and backed by dedicated regression tests. No public SDK surface changes; the memoryFileMaxChars opt-in is backward compatible.

No files require special attention.

Reviews (2): Last reviewed commit: "fix(memory-core): cap MEMORY.md size dur..." | Re-trigger Greptile

@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

Summary
This PR adds memory-core MEMORY.md budget compaction with regression coverage and changelog text, plus a maintainer-authored plugin-sdk progress-draft cap adjustment with matching tests.

Reproducibility: yes. Source inspection shows current main appends promoted sections to MEMORY.md without a budget while bootstrap caps per-file injection at 12,000 characters, and the PR supplies live before/after output using the exported promotion path.

Real behavior proof
Sufficient (live_output): The PR body and comments include after-fix live output from real exported applyShortTermPromotions runs against filesystem workspaces showing bounded size, compaction, and preserved user-authored notes.

Next step before merge
No repair-lane work is needed; the remaining action is normal maintainer merge validation and waiting for the queued checks on the latest head.

Security
Cleared: The diff changes local memory compaction logic, tests, changelog text, and a progress-line length constant; it adds no dependency, workflow, network, credential, permission, or code-execution surface.

Review details

Best possible solution:

Land the bounded compaction fix after normal CI and maintainer validation, while leaving configurable promotion target or archive design to #71330.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main appends promoted sections to MEMORY.md without a budget while bootstrap caps per-file injection at 12,000 characters, and the PR supplies live before/after output using the exported promotion path.

Is this the best way to solve the issue?

Yes. Compacting only older auto-promoted sections before the existing write is the narrow maintainable fix; the broader archive or configurable-target design is already represented by #71330.

Acceptance criteria:

  • pnpm test extensions/memory-core/src/memory-budget.test.ts extensions/memory-core/src/short-term-promotion.test.ts
  • pnpm test src/plugin-sdk/channel-streaming.test.ts
  • pnpm check:changed

What I checked:

Likely related people:

  • vignesh07: Merged feat(memory-core): add dreaming promotion with weighted recall thresholds #60569 introduced weighted short-term recall promotion, applyShortTermPromotions, dreaming integration, and related docs/tests. (role: introduced behavior; confidence: high; commits: 4c1022c73b39; files: extensions/memory-core/src/short-term-promotion.ts, extensions/memory-core/src/dreaming.ts, docs/concepts/dreaming.md)
  • steipete: Recent current-main history shows repeated memory-core and plugin-sdk touches, and the latest PR-head follow-up commits add changelog text and the progress-draft cap adjustment. (role: recent area contributor and PR-head follow-up; confidence: high; commits: 827b0de0ce74, b85b1c68d175, 82592c62a590; files: extensions/memory-core/src/short-term-promotion.ts, CHANGELOG.md, src/plugin-sdk/channel-streaming.ts)
  • mbelinky: Recent merged memory/dreaming work changed grounded backfill, REM preview, and promotion replay behavior in the same feature area. (role: adjacent dreaming contributor; confidence: medium; commits: e8209e4cf9b8, 79348f73c8b6; files: extensions/memory-core/src/short-term-promotion.ts, docs/concepts/dreaming.md)
  • gumadeiras: Merged work in the same memory-core dreaming path hardened self-ingestion behavior around promoted/dreaming material. (role: adjacent memory-core contributor; confidence: medium; commits: 0c4e0d703023; files: extensions/memory-core/src/short-term-promotion.ts, docs/concepts/dreaming.md)

Remaining risk / open question:

  • I did not execute tests locally in this read-only review; several GitHub checks for the latest head were still queued when sampled.
  • The branch includes a plugin-sdk progress-draft cap adjustment outside the memory-core fix, so maintainers should confirm that scope is intentional during merge validation.

Codex review notes: model gpt-5.5, reasoning high; reviewed against c20d45e34699.

Comment thread extensions/memory-core/src/memory-budget.ts
Comment thread extensions/memory-core/src/memory-budget.ts Outdated
@YB0y YB0y force-pushed the fix/memory-core-budget-promotion branch from bee897b to 7ef0834 Compare April 29, 2026 05:28
@YB0y

YB0y commented Apr 29, 2026

Copy link
Copy Markdown
Contributor Author

@greptile-apps would you please review again

@YB0y YB0y force-pushed the fix/memory-core-budget-promotion branch 6 times, most recently from 8abedd3 to e811c86 Compare April 30, 2026 00:33
@YB0y YB0y marked this pull request as draft May 1, 2026 21:07
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 6, 2026
@YB0y YB0y force-pushed the fix/memory-core-budget-promotion branch from 10c4737 to bf9013d Compare May 6, 2026 00:33
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 6, 2026
@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 6, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 6, 2026
@YB0y

YB0y commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

Real behavior — before/after live run

Same workload (30 promotion sweeps, ~800-char snippet each), real exported applyShortTermPromotions from this branch's working tree. memoryFileMaxChars: 0 disables compaction (mimics pre-fix code path); 10000 is the new default. Workspace pre-seeded with ## My Personal Notes to verify user-authored content is never dropped. Linux x86_64, Node 22.22.2, pnpm 10.33.0.

Workload: 30 promotion sweeps, each adding a ~800-char snippet.
Bootstrap per-file injection cap (the bug's root failure mode): 12000 bytes.


=== BEFORE FIX (memoryFileMaxChars: 0 — compaction disabled, mimics pre-fix code path) ===
iterations: 30
final MEMORY.md size: 30613 bytes
peak MEMORY.md size:  30613 bytes
iterations exceeding bootstrap per-file cap (12000): 19
total compacted sections: 0
user-authored content preserved on every iteration: true

  iter  date         size  applied  compacted  userNotes
     1  2026-04-01   1196        1          0  ok
     2  2026-04-02   2209        1          0  ok
     3  2026-04-03   3222        1          0  ok
     4  2026-04-04   4235        1          0  ok
     5  2026-04-05   5248        1          0  ok
     6  2026-04-06   6261        1          0  ok
     7  2026-04-07   7274        1          0  ok
     8  2026-04-08   8287        1          0  ok
     9  2026-04-09   9300        1          0  ok
    10  2026-04-10  10313        1          0  ok
    11  2026-04-11  11328        1          0  ok
    12  2026-04-12  12343        1          0  ok
    13  2026-04-13  13358        1          0  ok
    14  2026-04-14  14373        1          0  ok
    15  2026-04-15  15388        1          0  ok
    16  2026-04-16  16403        1          0  ok
    17  2026-04-17  17418        1          0  ok
    18  2026-04-18  18433        1          0  ok
    19  2026-04-19  19448        1          0  ok
    20  2026-04-20  20463        1          0  ok
    21  2026-04-21  21478        1          0  ok
    22  2026-04-22  22493        1          0  ok
    23  2026-04-23  23508        1          0  ok
    24  2026-04-24  24523        1          0  ok
    25  2026-04-25  25538        1          0  ok
    26  2026-04-26  26553        1          0  ok
    27  2026-04-27  27568        1          0  ok
    28  2026-04-28  28583        1          0  ok
    29  2026-05-01  29598        1          0  ok
    30  2026-05-02  30613        1          0  ok

=== AFTER FIX (memoryFileMaxChars: 10000 — default budget, fix active) ===
iterations: 30
final MEMORY.md size: 9318 bytes
peak MEMORY.md size:  9318 bytes
iterations exceeding bootstrap per-file cap (12000): 0
total compacted sections: 21
user-authored content preserved on every iteration: true

  iter  date         size  applied  compacted  userNotes
     1  2026-04-01   1196        1          0  ok
     2  2026-04-02   2209        1          0  ok
     3  2026-04-03   3222        1          0  ok
     4  2026-04-04   4235        1          0  ok
     5  2026-04-05   5248        1          0  ok
     6  2026-04-06   6261        1          0  ok
     7  2026-04-07   7274        1          0  ok
     8  2026-04-08   8287        1          0  ok
     9  2026-04-09   9300        1          0  ok
    10  2026-04-10   9300        1          1  ok
    11  2026-04-11   9302        1          1  ok
    12  2026-04-12   9304        1          1  ok
    13  2026-04-13   9306        1          1  ok
    14  2026-04-14   9308        1          1  ok
    15  2026-04-15   9310        1          1  ok
    16  2026-04-16   9312        1          1  ok
    17  2026-04-17   9314        1          1  ok
    18  2026-04-18   9316        1          1  ok
    19  2026-04-19   9318        1          1  ok
    20  2026-04-20   9318        1          1  ok
    21  2026-04-21   9318        1          1  ok
    22  2026-04-22   9318        1          1  ok
    23  2026-04-23   9318        1          1  ok
    24  2026-04-24   9318        1          1  ok
    25  2026-04-25   9318        1          1  ok
    26  2026-04-26   9318        1          1  ok
    27  2026-04-27   9318        1          1  ok
    28  2026-04-28   9318        1          1  ok
    29  2026-05-01   9318        1          1  ok
    30  2026-05-02   9318        1          1  ok

=== verdict ===
before-fix final size:  30613 bytes  (exceeded bootstrap cap: true)
after-fix  final size:  9318 bytes  (exceeded bootstrap cap: false)
reduction:  21295 bytes (69.6%)

Without the fix the file grows linearly past the 12 KB bootstrap cap by iteration 10 and finishes at 30,613 bytes after 30 sweeps. With the fix it stabilizes at 9,318 bytes once the budget is reached and never crosses the cap. User-authored ## My Personal Notes block survives all 30 iterations in both runs (the algorithm only ever drops auto-promoted sections).

Re-review progress:

@YB0y YB0y marked this pull request as ready for review May 6, 2026 01:18
@YB0y

YB0y commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

Hi, I verified this issue is remaining in main branch. Now real behavior testing is in progress.

@YB0y

YB0y commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

I've done live testing on fixed branch, here is result

==================== FILE 1: ~/dreaming-test-2026-05-06.log (timeline) ====================
[2026-05-06T12:50:24Z] === DREAMING REAL-LIFE TEST STARTED ===
[2026-05-06T12:50:24Z] branch=fix/memory-core-budget-promotion
[2026-05-06T12:50:24Z] version=  "version": "2026.5.5",
[2026-05-06T12:50:24Z] initial MEMORY.md size=565 bytes
[2026-05-06T12:50:24Z] size=565 bytes  recalls=0  promoted_sections=1
[2026-05-06T12:55:24Z] size=565 bytes  recalls=0  promoted_sections=1
[2026-05-06T13:00:24Z] size=565 bytes  recalls=0  promoted_sections=1
[2026-05-06T13:05:24Z] size=565 bytes  recalls=0  promoted_sections=1
[2026-05-06T13:10:24Z] size=565 bytes  recalls=0  promoted_sections=1
[2026-05-06T13:15:24Z] size=565 bytes  recalls=0  promoted_sections=1
[2026-05-06T13:18:13Z] === 24h DREAMING DRIVER STARTED (1 promotion/hour, size logged every 5 min) ===
[2026-05-06T13:18:13Z] branch=fix/memory-core-budget-promotion
[2026-05-06T13:18:13Z] version=  "version": "2026.5.5",
[2026-05-06T13:18:13Z] start MEMORY.md=565 bytes
[2026-05-06T13:18:13Z] === CYCLE 1 === token=cycle1-1518  size=565 bytes headings=1
[2026-05-06T13:18:29Z]  size=565  headings=1  recalls=0
[2026-05-06T13:20:50Z] === 24h DREAMING DRIVER v2 (cwd=/home/orin/Gittensor/OpenCoven/openclaw) ===
[2026-05-06T13:20:50Z] start MEMORY.md=870 bytes
[2026-05-06T13:20:50Z] === CYCLE 1 === token=cycle1-1520 appended=1 size=1115 headings=3 recalls=2
[2026-05-06T13:21:06Z]  size=1115  headings=3  recalls=2
[2026-05-06T13:22:34Z] === 24h DREAMING DRIVER v2 (cwd=/home/orin/Gittensor/OpenCoven/openclaw) ===
[2026-05-06T13:22:34Z] start MEMORY.md=1115 bytes
[2026-05-06T13:22:34Z] === CYCLE 1 === token=cycle1-1522  size=1115 headings=3 recalls=2
[2026-05-06T13:22:48Z]  size=1115  headings=3  recalls=2
[2026-05-06T13:23:51Z] === 24h DRIVER v3 (10-min cadence, ~400B/cycle) ===
[2026-05-06T13:23:51Z] start MEMORY.md=1115 bytes
[2026-05-06T13:23:51Z] CYCLE=1  size=1115 headings=3 recalls=3
[2026-05-06T13:27:22Z] === 24h DRIVER v4 (10-min, single-line snippet) ===
[2026-05-06T13:27:22Z] start MEMORY.md=1115 bytes
[2026-05-06T13:27:22Z] CYCLE=1 appended=1 size=1602 headings=4 recalls=4
[2026-05-06T13:37:36Z] CYCLE=2 appended=1 size=2089 headings=5 recalls=5
[2026-05-06T13:47:49Z] CYCLE=3 appended=1 size=2576 headings=6 recalls=6
[2026-05-06T13:58:03Z] CYCLE=4 appended=1 size=3063 headings=7 recalls=7
[2026-05-06T14:08:19Z] CYCLE=5 appended=1 size=3550 headings=8 recalls=8
[2026-05-06T14:18:33Z] CYCLE=6 appended=1 size=4037 headings=9 recalls=9
[2026-05-06T14:28:54Z] CYCLE=7 appended=1 size=4524 headings=10 recalls=10
[2026-05-06T14:39:10Z] CYCLE=8 appended=1 size=5011 headings=11 recalls=11
[2026-05-06T14:49:28Z] CYCLE=9 appended=1 size=5498 headings=12 recalls=12
[2026-05-06T14:59:43Z] CYCLE=10 appended=1 size=5987 headings=13 recalls=13
[2026-05-06T15:09:59Z] CYCLE=11 appended=1 size=6476 headings=14 recalls=14
[2026-05-06T15:20:19Z] CYCLE=12 appended=1 size=6965 headings=15 recalls=15
[2026-05-06T15:30:43Z] CYCLE=13 appended=1 size=7454 headings=16 recalls=16
[2026-05-06T15:41:04Z] CYCLE=14 appended=1 size=7943 headings=17 recalls=17
[2026-05-06T15:51:21Z] CYCLE=15 appended=1 size=8432 headings=18 recalls=18
[2026-05-06T16:01:37Z] CYCLE=16 appended=1 size=8921 headings=19 recalls=19
[2026-05-06T16:11:54Z] CYCLE=17 appended=1 size=9410 headings=20 recalls=20
[2026-05-06T16:22:11Z] CYCLE=18 appended=1 size=9899 headings=21 recalls=21
[2026-05-06T16:32:30Z] CYCLE=19 appended=1 size=9820 headings=20 recalls=22
[2026-05-06T16:42:44Z] CYCLE=20 appended=1 size=9577 headings=19 recalls=23
[2026-05-06T16:52:58Z] CYCLE=21 appended=1 size=9579 headings=19 recalls=24
[2026-05-06T17:03:12Z] CYCLE=22 appended=1 size=9581 headings=19 recalls=25
[2026-05-06T17:13:27Z] CYCLE=23 appended=1 size=9583 headings=19 recalls=26
[2026-05-06T17:23:43Z] CYCLE=24 appended=1 size=9585 headings=19 recalls=27
[2026-05-06T17:34:01Z] CYCLE=25 appended=1 size=9587 headings=19 recalls=28
[2026-05-06T17:44:17Z] CYCLE=26 appended=1 size=9589 headings=19 recalls=29
[2026-05-06T17:54:32Z] CYCLE=27 appended=1 size=9591 headings=19 recalls=30
[2026-05-06T18:04:45Z] CYCLE=28 appended=1 size=9593 headings=19 recalls=31
[2026-05-06T18:15:01Z] CYCLE=29 appended=1 size=9593 headings=19 recalls=32
[2026-05-06T18:25:22Z] CYCLE=30 appended=1 size=9593 headings=19 recalls=33
[2026-05-06T18:35:41Z] CYCLE=31 appended=1 size=9593 headings=19 recalls=34
[2026-05-06T18:45:56Z] CYCLE=32 appended=1 size=9593 headings=19 recalls=35
[2026-05-06T18:56:15Z] CYCLE=33 appended=1 size=9593 headings=19 recalls=36
[2026-05-06T19:06:31Z] CYCLE=34 appended=1 size=9593 headings=19 recalls=37
[2026-05-06T19:16:48Z] CYCLE=35 appended=1 size=9593 headings=19 recalls=38


==================== FILE 2: cycle 18→19→20 raw debug (compaction firing) ====================
[2026-05-06T16:22:11Z] cycle=18 seed=seed_ok

0.613 memory/2025-01-04.md:1-2
recalls=1 avg=0.950 queries=1 age=0.1d consolidate=0.20 conceptual=1.00
concepts=auto-test, promotion-budget, cycle1-1523, reference-data, auto, test, fact, promotion
Auto-test fact for promotion-budget cycle 1: cycle1-1523. Padding context: reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data

Processed 1 candidate(s) for ~/.openclaw/workspace/MEMORY.md.
appended=1 reconciledExisting=0
[2026-05-06T16:32:30Z] cycle=19 seed=seed_ok

0.613 memory/2025-01-04.md:1-2
recalls=1 avg=0.950 queries=1 age=0.1d consolidate=0.20 conceptual=1.00
concepts=auto-test, promotion-budget, cycle1-1523, reference-data, auto, test, fact, promotion
Auto-test fact for promotion-budget cycle 1: cycle1-1523. Padding context: reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data

Processed 1 candidate(s) for ~/.openclaw/workspace/MEMORY.md.
appended=1 reconciledExisting=0
[2026-05-06T16:42:44Z] cycle=20 seed=seed_ok

0.613 memory/2025-01-04.md:1-2
recalls=1 avg=0.950 queries=1 age=0.1d consolidate=0.20 conceptual=1.00
concepts=auto-test, promotion-budget, cycle1-1523, reference-data, auto, test, fact, promotion
Auto-test fact for promotion-budget cycle 1: cycle1-1523. Padding context: reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data reference-data

Processed 1 candidate(s) for ~/.openclaw/workspace/MEMORY.md.
appended=1 reconciledExisting=0
[2026-05-06T16:52:58Z] cycle=21 seed=seed_ok

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 6, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 6, 2026
@YB0y

YB0y commented May 10, 2026

Copy link
Copy Markdown
Contributor Author

hi @obviyus would you please review this PR?

@steipete steipete force-pushed the fix/memory-core-budget-promotion branch from b3709e4 to a54637a Compare May 11, 2026 13:07
steipete added a commit to YB0y/openclaw that referenced this pull request May 11, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 11, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 11, 2026
steipete added a commit to YB0y/openclaw that referenced this pull request May 11, 2026
@steipete steipete force-pushed the fix/memory-core-budget-promotion branch from a54637a to 1ed2c10 Compare May 11, 2026 13:31
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 11, 2026
steipete added a commit to YB0y/openclaw that referenced this pull request May 11, 2026
@steipete steipete force-pushed the fix/memory-core-budget-promotion branch from 1ed2c10 to 58ba58c Compare May 11, 2026 13:39
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 11, 2026
YB0y and others added 4 commits May 11, 2026 14:59
…event unbounded growth (openclaw#73691)

Dreaming's deep-phase promotion path appends to ~/.openclaw/workspace-<agent>/MEMORY.md
without a durable size budget. After weeks of use, the file grows past the bootstrap
injection cap (~12KB/file), at which point bootstrap silently truncates promoted memory
and (per the issue body) session writes can hit lock timeouts that wedge the gateway.

Adds a bounded compaction step in applyShortTermPromotions: before each write, drop the
OLDEST auto-promoted sections (date-ordered) until existing + new section fits within
memoryFileMaxChars (default 10,000 chars, safely below the 12KB bootstrap cap).
User-authored content is preserved unconditionally; only dreaming-owned sections are
eligible for compaction.

Verified:
- pnpm install --frozen-lockfile
- pnpm test extensions/memory-core/src/memory-budget.test.ts extensions/memory-core/src/short-term-promotion.test.ts
- pnpm exec oxfmt --check --threads=1 extensions/memory-core/src/memory-budget.ts extensions/memory-core/src/memory-budget.test.ts extensions/memory-core/src/short-term-promotion.ts extensions/memory-core/src/short-term-promotion.test.ts CHANGELOG.md
- pnpm check:changed
- pnpm tsgo:core
- pnpm tsgo:extensions
- pnpm tsgo:test:src
- git diff --check
- live driver: real applyShortTermPromotions across 5 sweeps with oversized seeded MEMORY.md — file stayed bounded, oldest sections compacted, user content preserved

Closes openclaw#73691
@steipete steipete force-pushed the fix/memory-core-budget-promotion branch from 58ba58c to d48623d Compare May 11, 2026 14:00
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 11, 2026
@steipete steipete merged commit 02eff52 into openclaw:main May 11, 2026
90 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Merged, thanks @YB0y.

Verification before landing:

  • pnpm test extensions/memory-core/src/memory-budget.test.ts extensions/memory-core/src/short-term-promotion.test.ts
  • pnpm test src/plugin-sdk/channel-streaming.test.ts -- --reporter=verbose
  • pnpm exec oxfmt --check --threads=1 src/plugin-sdk/channel-streaming.test.ts src/plugin-sdk/channel-streaming.ts CHANGELOG.md extensions/memory-core/src/memory-budget.ts extensions/memory-core/src/memory-budget.test.ts extensions/memory-core/src/short-term-promotion.ts extensions/memory-core/src/short-term-promotion.test.ts
  • git diff --check

Landed on main as 02eff52f16; local main synced to 8ee4290e06.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: memory-core Extension: memory-core proof: supplied External PR includes structured after-fix real behavior proof. size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: MEMORY.md grows unbounded → bootstrap overflow → Gateway freeze

2 participants