Skip to content

Fix cache prune scope for non-inbox runs#6

Merged
gmr merged 2 commits into
mainfrom
fix/cache-prune-scope
Jul 2, 2026
Merged

Fix cache prune scope for non-inbox runs#6
gmr merged 2 commits into
mainfrom
fix/cache-prune-scope

Conversation

@gmr

@gmr gmr commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Summary

Scope the daemon's seen-message cache prune to the folder each run
actually targets, instead of always listing in:inbox.

Problem

agent._load_and_prune_cache called client.list_inbox_message_ids()
(hardcoded q=in:inbox) and then seen.retain(inbox_ids) to drop
cached ids no longer present. For a non-inbox run — e.g.
use-agent run --query in:spam — none of the processed messages are
in:inbox, so every cached id was pruned on each cycle. The daemon
then re-investigated the same non-actioned messages every tick,
burning API calls and tokens. Acted-on messages were fine (they leave
the folder), but messages that stay put (e.g. NOT_COLD_SALES spam)
were re-classified endlessly.

Solution

  • Generalize GmailClient.list_inbox_message_ids to
    list_message_ids(query='in:inbox'), keeping its pagination and
    set[str] | None return contract.
  • Add a local agent._prune_query helper that derives the prune scope
    from the run's effective_query: the first in:<x> / label:<x>
    operand, falling back to in:inbox when the query names no folder.
    Default inbox runs remain byte-for-byte identical.
  • _load_and_prune_cache now takes the derived prune query and lists
    via the generalized method.
  • Tests: cover the query parameter + pagination on list_message_ids
    and the query→scope derivation across inbox/spam/label/lookback/no-folder cases.

ruff check, ruff format --check, and pytest -q (85 tests) all pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Cache cleanup now scopes pruning to the effective Gmail folder/label from your search query (defaulting to inbox when unspecified).
    • Gmail message listing is now query-driven, enabling flexible message lookup across folders and labels.
  • Bug Fixes

    • Improved pagination handling so message IDs are aggregated across multiple Gmail result pages.
    • Cache remains intact if refresh/listing fails, preventing unnecessary data loss.
    • Negated folder operands no longer incorrectly scope (or override) the effective prune scope.
  • Tests

    • Added coverage for query defaults, honoring provided queries, and pagination aggregation for message ID listing.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a563187-1442-4ea6-b391-3ae4f4cddfee

📥 Commits

Reviewing files that changed from the base of the PR and between 219256a and 8258335.

📒 Files selected for processing (4)
  • tests/test_gmail_headers.py
  • tests/test_prune_scope.py
  • use_agent/agent.py
  • use_agent/gmail.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • tests/test_prune_scope.py
  • tests/test_gmail_headers.py
  • use_agent/gmail.py
  • use_agent/agent.py

📝 Walkthrough

Walkthrough

GmailClient now lists messages with a query parameter, and agent.py derives a prune scope from the effective search query before pruning the seen-message cache. Tests cover query handling, pagination, and prune-scope selection.

Changes

Query-scoped Gmail listing and cache pruning

Layer / File(s) Summary
Generalize Gmail message listing
use_agent/gmail.py, tests/test_gmail_headers.py
list_message_ids(query='in:inbox') replaces inbox-only listing, uses the query in the Gmail API call, and updates pagination naming and warning text; tests verify default and paginated query behavior with a fake Gmail service.
Scope cache pruning from query
use_agent/agent.py, tests/test_prune_scope.py
A regex-based _prune_query() extracts the first in: or label: operand, run() passes that scope into _load_and_prune_cache, and cache pruning now lists message IDs for the scoped query; tests cover defaulting, precedence, and negated-folder cases.

Estimated code review effort: 2 (Simple) | ~12 minutes

Sequence Diagram(s)

sequenceDiagram
  participant run
  participant _prune_query
  participant _load_and_prune_cache
  participant GmailClient
  participant GmailAPI

  run->>_prune_query: effective_query
  _prune_query-->>run: prune_query
  run->>_load_and_prune_cache: client, prune_query
  _load_and_prune_cache->>GmailClient: list_message_ids(prune_query)
  GmailClient->>GmailAPI: users().messages().list(q=prune_query)
  GmailAPI-->>GmailClient: paginated ids or exception
  GmailClient-->>_load_and_prune_cache: ids or error
  _load_and_prune_cache-->>run: pruned cache
Loading

Possibly related PRs

  • gmr/use-agent#1: Introduced the seen-message cache and inbox-message listing that this PR generalizes into query-scoped listing and pruning.

Poem

A rabbit peeked at Gmail’s stream,
And chased the query through the beam.
With in: and label: in the hop,
The cache pruned clean—no inbox stop.
🐇📬

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: scoping cache pruning beyond inbox-only runs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
use_agent/gmail.py (1)

182-214: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Constants still named for "inbox" after generalization.

_INBOX_LIST_PAGE_CAP/_INBOX_LIST_PAGE_SIZE (used at Lines 193 and 200) are now used for arbitrary query values, not just inbox listing. Consider renaming to e.g. _LIST_PAGE_CAP/_LIST_PAGE_SIZE to match the generalized method.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@use_agent/gmail.py` around lines 182 - 214, The pagination constants in
list_message_ids are still named for inbox-only behavior even though the method
now supports any query. Rename _INBOX_LIST_PAGE_CAP and _INBOX_LIST_PAGE_SIZE to
query-neutral names like _LIST_PAGE_CAP and _LIST_PAGE_SIZE, and update their
uses in list_message_ids so the symbols match the generalized behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_prune_scope.py`:
- Line 3: The import in this test file uses a direct symbol import, which
violates the module-level import guideline. Update the top-level import to
import the use_agent module itself, then reference agent through that module
namespace wherever it is used in the test code.

In `@use_agent/agent.py`:
- Around line 407-423: The pruning scope extraction in _prune_query is
incorrectly matching negated operands like -in:spam or -label:x because
_PRUNE_OPERAND_RE allows a leading boundary match; update the regex or the
matching logic so a preceding negation is excluded before selecting the scope.
Keep the behavior in _prune_query the same for positive in:/label: operands and
the inbox fallback, but ensure only non-negated operands are returned.

---

Nitpick comments:
In `@use_agent/gmail.py`:
- Around line 182-214: The pagination constants in list_message_ids are still
named for inbox-only behavior even though the method now supports any query.
Rename _INBOX_LIST_PAGE_CAP and _INBOX_LIST_PAGE_SIZE to query-neutral names
like _LIST_PAGE_CAP and _LIST_PAGE_SIZE, and update their uses in
list_message_ids so the symbols match the generalized behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ddea8ee0-cbb5-4798-858a-64705c83df53

📥 Commits

Reviewing files that changed from the base of the PR and between eb79db3 and 219256a.

📒 Files selected for processing (4)
  • tests/test_gmail_headers.py
  • tests/test_prune_scope.py
  • use_agent/agent.py
  • use_agent/gmail.py

Comment thread tests/test_prune_scope.py
@@ -0,0 +1,31 @@
"""Derivation of the cache-prune folder scope from a run query."""

from use_agent import agent

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use module-level import instead of from ... import.

As per coding guidelines, **/*.py should "Use module-level imports only; do not use from ... import ... style imports. Import the module ... and reference members through the module namespace."

♻️ Proposed fix
-from use_agent import agent
+import use_agent.agent as agent
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from use_agent import agent
import use_agent.agent as agent
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_prune_scope.py` at line 3, The import in this test file uses a
direct symbol import, which violates the module-level import guideline. Update
the top-level import to import the use_agent module itself, then reference agent
through that module namespace wherever it is used in the test code.

Source: Coding guidelines

Comment thread use_agent/agent.py Outdated
gmr and others added 2 commits July 2, 2026 15:07
- _load_and_prune_cache hardcoded a list of in:inbox message-ids,
  then retained only those in the seen-cache. For a non-inbox run
  (e.g. --query in:spam) none of the processed messages are in:inbox,
  so every cached id was pruned each cycle.
- The daemon then re-investigated the same non-actioned messages
  every tick, wasting API calls and tokens. Acted-on messages were
  unaffected (they leave the folder); only messages that stay put
  (e.g. NOT_COLD_SALES spam) were re-classified endlessly.
- Generalize GmailClient.list_inbox_message_ids to list_message_ids
  (query param, default in:inbox), preserving pagination and the
  set[str] | None contract.
- Derive the prune scope from effective_query via a local _prune_query
  helper: take the first in:<x>/label:<x> operand, else fall back to
  in:inbox. Default inbox runs are byte-for-byte unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Exclude negated folder operands (e.g. -in:spam) from
  _PRUNE_OPERAND_RE so a query that excludes a folder doesn't get
  mis-scoped to prune that folder; add regression tests
- Rename _INBOX_LIST_PAGE_CAP/_INBOX_LIST_PAGE_SIZE to
  _LIST_PAGE_CAP/_LIST_PAGE_SIZE now that list_message_ids is used
  for arbitrary queries, not just the inbox

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@gmr gmr force-pushed the fix/cache-prune-scope branch from 219256a to 8258335 Compare July 2, 2026 19:09
@gmr gmr merged commit 0f11e5c into main Jul 2, 2026
2 checks passed
@gmr gmr deleted the fix/cache-prune-scope branch July 2, 2026 19:24
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.

1 participant