#20148: fix(memory): persist session dirty state and fix reindex gate
size: S
Cluster:
Memory Management Fixes
🤖 AI-assisted PR (Claude/OpenClaw agents). Fully tested, all changes reviewed and understood by human submitter.
Fixes two related issues causing session indexing to stop after gateway restart:
1. Move needsFullReindex check before reason gate in shouldSyncSessions()
2. Persist sessionsDirty flag to database metadata and rebuild dirty file set on startup
## Summary
- **Problem:** Session memory indexing silently stops after gateway restart — no errors, no warnings
- **Why it matters:** Users lose session search permanently until manual DB intervention; no workaround survives restarts
- **What changed:** Reordered logic gate in `shouldSyncSessions()`, added `sessionsDirty` persistence to DB meta, added `rebuildSessionsDirtyFiles()` on startup
- **What did NOT change:** Indexing logic, chunking, embedding pipeline, config schema — purely a state management fix
## Change Type (select all)
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [ ] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [x] Memory / storage
- [ ] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
- Related #6600
- Related #3558
## User-visible / Behavior Changes
- Session memory indexing now resumes correctly after gateway restart (previously silently stopped)
- No config changes, no new options, no migration needed
## 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`
## Repro + Verification
### Environment
- OS: Ubuntu 22.04
- Runtime/container: Node.js v22
- Model/provider: Local embeddings (nomic-embed-text-v1.5 GGUF)
- Relevant config:
```json
{
"memorySearch": {
"sources": ["memory", "sessions"],
"experimental": { "sessionMemory": true },
"sync": { "intervalMinutes": 5 }
}
}
```
### Steps
1. Enable session memory with interval sync (config above)
2. Let interval sync index some sessions — verify with `openclaw memory status`
3. Restart gateway: `openclaw gateway restart`
4. Check `openclaw memory status` over the next interval cycle
### Expected
- Session file count continues increasing after restart
### Actual
- Session count freezes, shows `Dirty: yes` but no indexing activity
## Evidence
- [x] Failing test/log before + passing after
Two new tests in `src/memory/index.test.ts`:
1. **Reindex gate order** — calls `shouldSyncSessions({ reason: "session-start" }, true)` and asserts `true`. Before the fix this returned `false`. Also verifies negative case.
2. **Dirty state persistence** — writes meta with `sessionsDirty: true`, closes manager, reopens, asserts dirty state is restored.
All 10 tests passing, linter clean.
## Human Verification (required)
- Verified: `shouldSyncSessions()` gate order — `needsFullReindex` now checked before `reason`
- Verified: `sessionsDirty` added to `MemoryIndexMeta` type
- Verified: Persistence write in `runSafeReindex` and restore in constructor
- Verified: `rebuildSessionsDirtyFiles()` queries existing `files` table with correct schema
- Edge cases: Empty sessions dir (empty Set, no-op), missing meta field (optional, defaults falsy)
- **Not verified:** Full end-to-end reindex cycle on live system (unit tests only)
## Compatibility / Migration
- Backward compatible? `Yes`
- Config/env changes? `No`
- Migration needed? `No`
## Failure Recovery (if this breaks)
- How to revert: Revert commit, restart gateway
- Files to restore: `src/memory/manager-sync-ops.ts`, `src/memory/manager.ts`
- Bad symptoms: If `rebuildSessionsDirtyFiles()` fails, logs warning and continues — does not block startup
## Risks and Mitigations
- Risk: `rebuildSessionsDirtyFiles()` scans all session files on startup, could be slow with thousands of files
- Mitigation: Runs async (`void this.rebuildSessionsDirtyFiles()`), does not block constructor or first sync
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes two related bugs that caused session memory indexing to silently stop after gateway restart:
- **Reindex gate order fix** (`manager-sync-ops.ts`): Moves the `needsFullReindex` check before the `reason` gate in `shouldSyncSessions()`. Previously, `reason === "session-start"` or `"watch"` would short-circuit and return `false` even when a full reindex was needed, preventing session indexing from ever completing after a restart.
- **Dirty state persistence** (`manager.ts`, `manager-sync-ops.ts`): Adds `sessionsDirty` to the `MemoryIndexMeta` type and persists it to DB metadata during `runSafeReindex`. On startup, the constructor restores this flag and calls `rebuildSessionsDirtyFiles()` to rediscover unindexed session files by comparing the file system against the `files` table.
- Two new tests cover both fixes with positive and negative assertions.
The changes are well-scoped and backward-compatible. One minor inconsistency: `runUnsafeReindex` (test-only path) does not persist `sessionsDirty` to meta, unlike `runSafeReindex`.
<h3>Confidence Score: 4/5</h3>
- This PR is safe to merge — it fixes a clear logic ordering bug and adds state persistence with graceful fallbacks.
- The gate reorder fix is straightforward and correct. The persistence mechanism is sound — `rebuildSessionsDirtyFiles` is wrapped in try/catch and runs as fire-and-forget, so startup isn't blocked if it fails. Tests cover both changes. Minor deduction for the `runUnsafeReindex` inconsistency (test-only) and the fact that `sessionsDirty` is only persisted during full reindex, not during regular sync cycles (mitigated by the rebuild-on-startup approach).
- `src/memory/manager-sync-ops.ts` — `runUnsafeReindex` should be updated for consistency with the new `sessionsDirty` persistence in `runSafeReindex`.
<sub>Last reviewed commit: eca6eca</sub>
<!-- greptile_other_comments_section -->
<sub>(3/5) Reply to the agent's comments like "Can you suggest a fix for this @greptileai?" or ask follow-up questions!</sub>
**Context used:**
- Context from `dashboard` - CLAUDE.md ([source](https://app.greptile.com/review/custom-context?memory=fd949e91-5c3a-4ab5-90a1-cbe184fd6ce8))
<!-- /greptile_comment -->
Most Similar PRs
#11749: fix(memory): prioritize full reindex over session-start skip in sho...
by QDenka · 2026-02-08
87.2%
#4386: fix(memory): persist dirty flag to prevent false positive on status
by Iamadig · 2026-01-30
82.4%
#20149: fix(memory): expose index concurrency as config option
by togotago · 2026-02-18
82.2%
#17639: fix: Memory indexer skips session files
by MisterGuy420 · 2026-02-16
79.3%
#20183: fix(memory): index reset/deleted session transcripts
by VACInc · 2026-02-18
78.4%
#6653: fix: persist archived session entry on /new or /reset
by leicao-me · 2026-02-01
78.0%
#6315: fix(cron): persist isolated sessions (fixes #6217) - attempt 2
by ChaitanyaSai-Meka · 2026-02-01
77.9%
#22525: [Bug]: Session snapshot not reloading skills after gateway restart ...
by zwffff · 2026-02-21
77.5%
#20431: fix(sessions): add session contamination guards and self-leak lock ...
by marcomarandiz · 2026-02-18
77.0%
#22568: fix(gateway): bump skills snapshot version on startup so sessions r...
by zwffff · 2026-02-21
76.9%