UAT: BoundedMemorySaver._prune() accesses private LangGraph MemorySaver internals via cast(Any, self), creating fragile coupling to LangGraph implementation details #3840

Open
opened 2026-04-06 06:54:28 +00:00 by freemo · 0 comments
Owner

Metadata

  • Branch: fix/bounded-memory-saver-public-api
  • Commit Message: fix(memory): replace cast(Any, self) private-attribute access in BoundedMemorySaver._prune with public LangGraph API
  • Milestone: (none — backlog)
  • Parent Epic: #362

What was tested

The BoundedMemorySaver._prune() method in src/cleveragents/agents/graphs/plan_generation.py.

Expected behavior (from spec)

The spec requires LangGraph checkpointing for plan state persistence. The BoundedMemorySaver is intended to limit memory usage by pruning old checkpoints. It should do so using the public LangGraph API only, in accordance with the project's strict type-safety policy (CONTRIBUTING.md forbids cast(Any, ...) and # type: ignore patterns that suppress type checking).

Actual behavior

The BoundedMemorySaver._prune() method in src/cleveragents/agents/graphs/plan_generation.py (lines 40–75) accesses private internal attributes of LangGraph's MemorySaver class using cast(Any, self) to bypass Pyright type checking:

def _prune(self, thread_id: str, checkpoint_ns: str) -> None:
    storage = cast(Any, self).storage    # Private attribute
    blobs = cast(Any, self).blobs        # Private attribute
    writes = cast(Any, self).writes      # Private attribute
    serde = cast(Any, self).serde        # Private attribute
    ...

This is problematic because:

  1. storage, blobs, writes, and serde are private implementation details of MemorySaver that are not part of the public LangGraph API.
  2. These attributes could be renamed, restructured, or removed in any LangGraph update (the project targets LangGraph 0.2.60 per the spec).
  3. The cast(Any, self) pattern is used specifically to bypass Pyright type checking — CONTRIBUTING.md explicitly forbids suppressing or disabling type checking via any mechanism.
  4. If LangGraph changes its internal storage format, _prune() will silently fail or corrupt checkpoint data.

Code location

  • src/cleveragents/agents/graphs/plan_generation.py lines 40–75 (BoundedMemorySaver._prune() method)

Steps to reproduce

  1. Run the plan generation graph with checkpoint_limit=2
  2. Perform 3+ state updates
  3. The _prune() method is called via put() override
  4. If LangGraph updates its internal storage structure, this will break silently

Impact

This creates a fragile dependency on LangGraph's private implementation details. Any LangGraph version update could break checkpoint pruning, potentially causing memory issues in long-running plan executions. The cast(Any, self) pattern also suppresses type safety that would otherwise catch issues — a direct violation of CONTRIBUTING.md's type-safety policy.

Backlog note: This issue was discovered during autonomous operation
on milestone v3.5.0 (LangGraph and LangChain Integration feature area). It does not block milestone completion and has been
placed in the backlog for human review and future milestone assignment.

Subtasks

  • Audit BoundedMemorySaver._prune() in src/cleveragents/agents/graphs/plan_generation.py lines 40–75 and identify all cast(Any, self) usages
  • Research the public LangGraph 0.2.60 MemorySaver API for supported checkpoint listing and deletion methods
  • Rewrite _prune() to use only the public LangGraph API (e.g., alist(), adelete_thread(), or equivalent public methods)
  • Remove all cast(Any, self) patterns from BoundedMemorySaver
  • Verify the rewritten _prune() passes Pyright (nox -e typecheck) with no suppressions
  • Update or add Behave unit tests in features/ covering the pruning logic with the new public API
  • Run nox (all default sessions) and fix any failures
  • Verify coverage ≥ 97% via nox -e coverage_report

Definition of Done

  • All cast(Any, self) usages removed from BoundedMemorySaver
  • _prune() uses only the public LangGraph MemorySaver API
  • nox -e typecheck passes with zero Pyright errors or suppressions in the affected file
  • Behave unit tests cover the pruning behaviour end-to-end
  • A Git commit is created where the first line of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional details about the implementation
  • The commit is pushed to the remote on the branch matching the Branch in Metadata exactly
  • The commit is submitted as a pull request to master, reviewed, and merged before this issue is marked done
  • All nox stages pass
  • Coverage ≥ 97%

Automated by CleverAgents Bot
Supervisor: UAT Testing | Agent: ca-new-issue-creator

## Metadata - **Branch**: `fix/bounded-memory-saver-public-api` - **Commit Message**: `fix(memory): replace cast(Any, self) private-attribute access in BoundedMemorySaver._prune with public LangGraph API` - **Milestone**: *(none — backlog)* - **Parent Epic**: #362 ## What was tested The `BoundedMemorySaver._prune()` method in `src/cleveragents/agents/graphs/plan_generation.py`. ## Expected behavior (from spec) The spec requires LangGraph checkpointing for plan state persistence. The `BoundedMemorySaver` is intended to limit memory usage by pruning old checkpoints. It should do so using the **public** LangGraph API only, in accordance with the project's strict type-safety policy (CONTRIBUTING.md forbids `cast(Any, ...)` and `# type: ignore` patterns that suppress type checking). ## Actual behavior The `BoundedMemorySaver._prune()` method in `src/cleveragents/agents/graphs/plan_generation.py` (lines 40–75) accesses private internal attributes of LangGraph's `MemorySaver` class using `cast(Any, self)` to bypass Pyright type checking: ```python def _prune(self, thread_id: str, checkpoint_ns: str) -> None: storage = cast(Any, self).storage # Private attribute blobs = cast(Any, self).blobs # Private attribute writes = cast(Any, self).writes # Private attribute serde = cast(Any, self).serde # Private attribute ... ``` This is problematic because: 1. `storage`, `blobs`, `writes`, and `serde` are private implementation details of `MemorySaver` that are not part of the public LangGraph API. 2. These attributes could be renamed, restructured, or removed in any LangGraph update (the project targets LangGraph 0.2.60 per the spec). 3. The `cast(Any, self)` pattern is used specifically to bypass Pyright type checking — CONTRIBUTING.md explicitly forbids suppressing or disabling type checking via any mechanism. 4. If LangGraph changes its internal storage format, `_prune()` will silently fail or corrupt checkpoint data. ## Code location - `src/cleveragents/agents/graphs/plan_generation.py` lines 40–75 (`BoundedMemorySaver._prune()` method) ## Steps to reproduce 1. Run the plan generation graph with `checkpoint_limit=2` 2. Perform 3+ state updates 3. The `_prune()` method is called via `put()` override 4. If LangGraph updates its internal storage structure, this will break silently ## Impact This creates a fragile dependency on LangGraph's private implementation details. Any LangGraph version update could break checkpoint pruning, potentially causing memory issues in long-running plan executions. The `cast(Any, self)` pattern also suppresses type safety that would otherwise catch issues — a direct violation of CONTRIBUTING.md's type-safety policy. > **Backlog note:** This issue was discovered during autonomous operation > on milestone v3.5.0 (LangGraph and LangChain Integration feature area). It does not block milestone completion and has been > placed in the backlog for human review and future milestone assignment. ## Subtasks - [ ] Audit `BoundedMemorySaver._prune()` in `src/cleveragents/agents/graphs/plan_generation.py` lines 40–75 and identify all `cast(Any, self)` usages - [ ] Research the public LangGraph 0.2.60 `MemorySaver` API for supported checkpoint listing and deletion methods - [ ] Rewrite `_prune()` to use only the public LangGraph API (e.g., `alist()`, `adelete_thread()`, or equivalent public methods) - [ ] Remove all `cast(Any, self)` patterns from `BoundedMemorySaver` - [ ] Verify the rewritten `_prune()` passes Pyright (`nox -e typecheck`) with no suppressions - [ ] Update or add Behave unit tests in `features/` covering the pruning logic with the new public API - [ ] Run `nox` (all default sessions) and fix any failures - [ ] Verify coverage ≥ 97% via `nox -e coverage_report` ## Definition of Done - [ ] All `cast(Any, self)` usages removed from `BoundedMemorySaver` - [ ] `_prune()` uses only the public LangGraph `MemorySaver` API - [ ] `nox -e typecheck` passes with zero Pyright errors or suppressions in the affected file - [ ] Behave unit tests cover the pruning behaviour end-to-end - [ ] A Git commit is created where the **first line** of the commit message matches the Commit Message in Metadata exactly, followed by a blank line, then additional details about the implementation - [ ] The commit is pushed to the remote on the branch matching the **Branch** in Metadata exactly - [ ] The commit is submitted as a **pull request** to `master`, reviewed, and **merged** before this issue is marked done - [ ] All nox stages pass - [ ] Coverage ≥ 97% --- **Automated by CleverAgents Bot** Supervisor: UAT Testing | Agent: ca-new-issue-creator
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Blocks
#362 Epic: Security & Safety Hardening
cleveragents/cleveragents-core
Reference
cleveragents/cleveragents-core#3840
No description provided.