Skip to content

Add cache TTL and max hits to zsh completion caching#360

Open
nat-n wants to merge 7 commits intodevelopmentfrom
claude/fix-zsh-cache-ttl-QSYB3
Open

Add cache TTL and max hits to zsh completion caching#360
nat-n wants to merge 7 commits intodevelopmentfrom
claude/fix-zsh-cache-ttl-QSYB3

Conversation

@nat-n
Copy link
Owner

@nat-n nat-n commented Feb 12, 2026

Description of changes

This PR enhances the zsh completion caching mechanism with two important features to prevent serving stale data:

1. In-Memory Cache TTL (Time-To-Live)

  • Adds timestamp tracking for in-memory cached task descriptions and arguments
  • Implements a 1-hour TTL (matching the disk cache policy) for in-memory cache entries
  • Prevents indefinitely serving stale data from in-memory cache within long-lived shell sessions
  • Tracks timestamps using zsh's $SECONDS variable in _poe_mem_tasks_time and _poe_mem_args_time associative arrays

2. Max Cache Hits Threshold

  • Introduces a hit counter mechanism that forces a fresh fetch after N cache hits (default: 10)
  • Applies to both task descriptions and task arguments caching
  • Resets counter to 0 after a forced refresh, allowing cache to be used again
  • Prevents serving stale cached data indefinitely even when disk cache TTL hasn't expired
  • Tracks hits in _poe_cache_hits_tasks and _poe_cache_hits_args associative arrays

3. Cache Policy Pattern Fix

  • Fixes cache policy zstyle pattern to use wildcard matching (":completion:*:${0#_}*:*")
  • Ensures cache policy is discoverable after _arguments -C modifies curcontext
  • Previously, the literal context pattern wouldn't match when _arguments -C appended state suffixes (e.g., poepoe-args)

4. Code Refactoring

  • Extracts task fetching logic into a new _poe_fetch_tasks() helper function
  • Consolidates hybrid caching logic (disk → in-memory → fresh fetch) in one place
  • Reduces code duplication between task state and help_task state handlers
  • Improves maintainability and testability

Testing

  • Adds comprehensive test suite covering:
    • Cache policy pattern matching across different curcontext states
    • In-memory cache TTL expiration behavior
    • Max cache hits threshold enforcement
    • Cache hit counter reset after forced refresh
    • Behavior for both task descriptions and task arguments
    • Help task state caching

All new features are covered by tests in test_zsh_completion_harness.py.

Pre-merge Checklist

  • New features are covered by new feature tests
  • Bug fixes are accompanied by tests
  • Code changes follow existing patterns and conventions
  • This PR targets the development branch

https://claude.ai/code/session_017koSbdzwCoQ2evAwnRkpbN

The in-memory cache (associative arrays _poe_mem_tasks/_poe_mem_args)
had no TTL, causing it to serve stale data indefinitely within a shell
session. When the disk cache expired after 1 hour, the code fell through
to the in-memory fallback which had no expiration check, effectively
making the cache permanent for the lifetime of the shell.

Fix by tracking timestamps via $SECONDS alongside cached data and
checking age against _POE_CACHE_TTL (3600s) on retrieval. This applies
to all three cache consumers: task state, help_task state, and task args.

https://claude.ai/code/session_017koSbdzwCoQ2evAwnRkpbN
…changes

The cache-policy was registered with zstyle using the literal
${curcontext} value. But _arguments -C modifies curcontext when entering
states (e.g. "poe" becomes "poe-args"), so when _cache_invalid later
looked up the policy with the modified context, it didn't match. Without
finding a policy, _cache_invalid only checks file existence — the disk
cache was never invalidated, even across new shell sessions.

Fix by using a wildcard pattern ":completion:*:${0#_}*:*" that matches
all poe-related contexts regardless of state suffix.

https://claude.ai/code/session_017koSbdzwCoQ2evAwnRkpbN
Test coverage for two bugs where the cache TTL wasn't working:

1. test_cache_policy_found_after_arguments_modifies_context: Verifies
   the zstyle cache-policy pattern matches even after _arguments -C
   modifies curcontext (e.g., poe -> poe-args). Fails with the old
   ":completion:${curcontext}:" pattern.

2. test_expired_in_memory_cache_triggers_fresh_fetch: Verifies that
   in-memory task cache with expired TTL triggers a fresh fetch.
   Fails without the SECONDS-based TTL check.

3. test_valid_in_memory_cache_is_used: Control test verifying
   in-memory cache within TTL is used (passes with both old/new code).

4. test_expired_in_memory_args_cache_triggers_fresh_fetch: Verifies
   task args in-memory cache also respects TTL. Fails without fix.

https://claude.ai/code/session_017koSbdzwCoQ2evAwnRkpbN
…hells

In a long-running shell session, cached completion data could be served
indefinitely if the disk cache TTL never expires (e.g., file mtime stays
fresh due to other processes). This adds a per-key hit counter that forces
a fresh fetch after every 10 cache hits, regardless of TTL status.

Applies uniformly to both disk and in-memory caches for task lists, task
args, and help_task completions. Counter resets to 0 on each fresh fetch.

https://claude.ai/code/session_017koSbdzwCoQ2evAwnRkpbN
The task_state_handler and help_task_state_handler had identical 50-line
cache blocks (disk -> in-memory -> hit counting -> fresh fetch -> store).
Extract into a _poe_fetch_tasks() zsh helper function that both handlers
call, returning results via the standard zsh reply array.

Net -28 lines. Args handler left inline since its cache key structure
(path|task) and data format differ enough to not warrant unification.

https://claude.ai/code/session_017koSbdzwCoQ2evAwnRkpbN
Black 25.11.0 prefers expanded multiline argument formatting. The
previous formatter commit (978d247) collapsed these, which was the
opposite of what the current black version wants.

https://claude.ai/code/session_017koSbdzwCoQ2evAwnRkpbN
@nat-n nat-n changed the base branch from main to development February 15, 2026 22:12
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.

2 participants

Comments