Skip to content

fix(checkpoint): repair stale session cursors after cleanup#324

Open
Arreboi06 wants to merge 5 commits into
TencentCloud:mainfrom
Arreboi06:fix/checkpoint-recalibration
Open

fix(checkpoint): repair stale session cursors after cleanup#324
Arreboi06 wants to merge 5 commits into
TencentCloud:mainfrom
Arreboi06:fix/checkpoint-recalibration

Conversation

@Arreboi06

@Arreboi06 Arreboi06 commented Jun 30, 2026

Copy link
Copy Markdown

Related to #157. Complements #177.

Summary

This PR keeps #177 as the canonical aggregate checkpoint-counter recalibration fix, and narrows this branch to the additional behavior that #177 does not cover: repairing stale per-session cursors after cleanup/startup.

The extra failure mode is not just a wrong displayed count. After JSONL/DB cleanup, a session cursor can point past the newest retained record for that same session. When the scheduler restores from that stale checkpoint, incremental L1/L2 reads can skip retained or newly written data.

Relationship to #177

#177 should remain the reference fix for the aggregate counter drift in #157:

  • recalibrate checkpoint counters against actual store counts;
  • adjust memories_since_last_persona safely;
  • run recalibration after cleanup and before scheduler startup.

This branch now follows that same safety model. In particular, memories_since_last_persona is adjusted only by the number of removed L1 memories. It is not recomputed from last_persona_at, because last_persona_at is a total_processed cursor, not an L1 memory counter.

Intentional Behavior Beyond #177

This PR adds session-aware cursor recovery:

  • clamp runner_states[session].last_l1_cursor to that session's newest retained L0 recordedAt watermark;
  • clamp runner_states[session].last_captured_timestamp only when retained L0 evidence proves it is stale;
  • clamp pipeline_states[session].last_extraction_updated_time to that session's newest retained L1 updatedAt / updated_time watermark;
  • reset a stale session cursor only when the scan can safely trust that the session has no retained records;
  • restore the scheduler from the recalculated checkpoint so repaired pipeline cursors are used immediately.

The key point is that cursor correction is per-session, not global. A global newest timestamp can hide a stale cursor in one session when another session has newer retained data.

Implementation Notes

  • recalibrate() handles explicit count reconciliation when callers already know actual counts.
  • recalculate() scans retained JSONL shards, accepts optional VectorStore counts, updates aggregate counters, and repairs per-session cursors from retained watermarks.
  • LocalMemoryCleaner.runOnce() invokes recalculation after JSONL/DB cleanup.
  • Scheduler startup recalculates before restoring pipeline states.
  • Correction stats are returned to callers but are not persisted into recall_checkpoint.json.

Verification

Latest verified commit: cd026e6

npm test -- src/utils/checkpoint.test.ts
Test Files  1 passed (1)
Tests       50 passed (50)
npm test
Test Files  5 passed (5)
Tests       117 passed (117)
npm run build
Build complete
scripts/migrate-sqlite-to-tcvdb: passed
scripts/export-tencent-vdb: passed
scripts/read-local-memory: passed
git diff --check
passed

High-Value Scenarios Covered

  • manual JSONL pruning recalibrates aggregate counters;
  • automatic cleaner recalibrates after deleting expired shards;
  • memories_since_last_persona is reduced by removed L1 memories, not by mixing L1 counts with last_persona_at;
  • stale runner cursors are clamped by the same session's retained L0 watermark;
  • stale pipeline cursors are clamped by the same session's retained L1 watermark;
  • sessions without retained data are reset only when the scan evidence is trustworthy;
  • missing directories and malformed JSONL lines are handled without failing recalculation.

…l data

After cleanup operations (deleting test pipeline states, running
memory-cleaner, or manual JSONL pruning), checkpoint counters drift
from actual data because all increment methods lack decrement counterparts.

This adds a recalibrate() method that allows callers to reset counters
to their actual values by providing the true counts from the data layer.
The method recalculates:
- l0_conversations_count
- total_memories_extracted
- total_processed
- memories_since_last_persona (derived from last_persona_at)

Fixes: TencentCloud#157
Co-authored-by: Cursor <cursoragent@cursor.com>
@Arreboi06 Arreboi06 force-pushed the fix/checkpoint-recalibration branch from 7ed86cb to 9cacd0e Compare June 30, 2026 17:50
@Arreboi06 Arreboi06 changed the title fix(checkpoint): add recalibrate() method to sync counters with actual data fix(checkpoint): add recalibrate() method to sync counters with actual data (Fix #157) Jun 30, 2026
Arreboi06 and others added 2 commits July 1, 2026 03:22
This commit enhances the checkpoint recalibration PR with:

## Documentation (checkpoint.ts)
- Complete checkpoint data flow diagram showing:
  - L0/L1/L2/L3 pipeline paths
  - Cleanup scenarios and counter drift causes
  - Solution implementation (recalibrate + decrement methods)
- Counter drift impact analysis table
- Design pattern analysis (event-sourced, storage-first, hybrid)
- Acceptance criteria coverage matrix

## Test Coverage (checkpoint.test.ts)
New test suites covering:

1. Cleanup Scenarios
   - Manual JSONL pruning (user deletes old shards)
   - Test data cleanup (developer removes test records)
   - Session reset (user resets a specific session)
   - Incremental processing (prevents L1/L2 from skipping records)
   - Automatic cleaner integration (memory-cleaner.runOnce)

2. Design Pattern Validation
   - Hybrid cursor-first approach
   - Backward compatibility
   - Atomic concurrent operations

Total: 37 tests (previously 17), all passing

Fixes: TencentCloud#157
Co-authored-by: Cursor <cursoragent@cursor.com>
…tup/cleaner

This commit adds the complete implementation to fix checkpoint counter drift:

## New Features

### 1. recalculate() auto-scan method
- Automatically scans conversations/YYYY-MM-DD.jsonl for L0 counts
- Automatically scans records/YYYY-MM-DD.jsonl for L1 counts
- Clamps stale last_l1_cursor to newest retained L0 recordedAt
- Clamps stale last_extraction_updated_time to newest retained L1 updatedAt
- Resets session cursors to 0/"" for sessions with no retained data
- Returns cursor correction statistics

### 2. Startup integration (TdaiCore.ensureSchedulerStarted)
- Calls recalculate() before scheduler starts
- Uses VectorStore counts when available (preferred over JSONL scan)
- Non-fatal: proceeds with stale checkpoint if recalculation fails

### 3. Memory-cleaner integration (LocalMemoryCleaner.runOnce)
- Recalculates checkpoint after cleanup completes
- Supports optional checkpoint manager injection
- Supports optional callback for recalculation results

## Test Coverage
- 44 unit tests (added 7 new tests for recalculate())
- Covers auto-scan, cursor correction, store count override
- All tests pass

Fixes: TencentCloud#157
Co-authored-by: Cursor <cursoragent@cursor.com>
@Yuntong8888

Copy link
Copy Markdown
Collaborator

Hi @Arreboi06 👋,
Thank you for submitting this PR and participating in Tencent Rhino-bird Open-source Training Program!
We have successfully received your submission. The program is currently in full swing, and we will complete the Code Review for you as soon as possible. Please keep an eye on the status notifications for this PR so you can follow up promptly once the review feedback is provided.
Thanks again for your contribution and open-source spirit! 🚀

coder-mtj added a commit to coder-mtj/TencentDB-Agent-Memory that referenced this pull request Jul 7, 2026
…llback, concurrency, crash recovery, and cleaner integration

- Extend RecalibrateOptions with totalProcessedCount and memoriesSincePersonaCount (absorb PR TencentCloud#253 L2ncE)
- Add RecalibrationResult interface with before/after snapshots (absorb PR TencentCloud#324 Arreboi06)
- Add integration tests: JSONL end-to-end, 10k-line perf, corrupt recovery (9 tests)
- Add concurrent safety tests: lock sharing, multi-instance, capture+recalibrate atomicity (5 tests)
- Add crash recovery tests: atomic write, tmp residue, truncated JSON (5 tests)
- Add memory-cleaner integration: onAfterCleanup -> recalibrate chain (5 tests)
- Total: 54 checkpoint tests (+34 new), 121 full suite
@Arreboi06 Arreboi06 changed the title fix(checkpoint): add recalibrate() method to sync counters with actual data (Fix #157) fix(checkpoint): recalculate counters and session cursors after cleanup Jul 8, 2026
@YOMXXX

YOMXXX commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Triage note: #177 has been locally verified and approved as the canonical #157 fix. It recalibrates checkpoint counters against actual store counts, adjusts memories_since_last_persona safely, and wires recalibration after cleanup plus scheduler startup. This PR appears to cover the same checkpoint-counter drift scope, so I recommend treating it as superseded unless it intentionally adds behavior beyond #177.

@Arreboi06

Arreboi06 commented Jul 9, 2026

Copy link
Copy Markdown
Author

Addressed the superseded-scope concern in the latest push (cd026e6).

I agree that #177 is the canonical fix for the aggregate checkpoint-counter drift in #157. This branch now keeps that same safety model: memories_since_last_persona is adjusted only by the number of removed L1 memories, not recomputed from last_persona_at, because last_persona_at tracks total_processed and is not an L1-memory counter.

The intentional behavior beyond #177 is narrower: session-aware cursor repair. Cleanup/startup can leave per-session cursors ahead of retained data:

  • runner_states[session].last_l1_cursor can point past the newest retained L0 record, causing L1 incremental reads to skip retained/new records.
  • pipeline_states[session].last_extraction_updated_time can point past the newest retained L1 record, causing L2 incremental reads to skip records.

recalculate() now keeps the aggregate counter recalibration semantics aligned with #177, and additionally clamps only stale per-session cursors using retained shard watermarks. It is wired after cleanup and before scheduler restore so the scheduler starts from repaired pipeline state.

Local verification after this update:

  • npm test -- src/utils/checkpoint.test.ts - 50 passed
image image image
  • npm test - 117 passed
image
  • npm run build - passed
image image

So I would frame this PR as: #177 fixes the canonical aggregate counter drift; this PR is only worth keeping if the maintainers want the extra session-cursor recovery behavior as a follow-up on top of that fix.

@Arreboi06 Arreboi06 changed the title fix(checkpoint): recalculate counters and session cursors after cleanup fix(checkpoint): repair stale session cursors after cleanup Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants