Skip to content

fix(config-cache): has() no longer inflates hit/miss statistics#498

Open
nikolasdehor wants to merge 1 commit intoSynkraAI:mainfrom
nikolasdehor:fix/config-cache-has-stats-inflation
Open

fix(config-cache): has() no longer inflates hit/miss statistics#498
nikolasdehor wants to merge 1 commit intoSynkraAI:mainfrom
nikolasdehor:fix/config-cache-has-stats-inflation

Conversation

@nikolasdehor
Copy link
Contributor

@nikolasdehor nikolasdehor commented Feb 24, 2026

Resumo

  • Reimplementa has() no ConfigCache para não chamar get() internamente
  • Antes: has() delegava para get(), que incrementava contadores de hits/misses — uma simples verificação de existência inflava as estatísticas do cache
  • Depois: has() verifica diretamente no Map sem tocar nas métricas

Closes #497

Plano de teste

  • 35 testes unitários passam localmente
  • Testes de regressão verificam que has() não altera stats de hits/misses
  • Teste confirma que has() seguido de get() conta exatamente 1 hit

has() delegated to get(), which increments this.hits or this.misses.
The common has()+get() pattern double-counted every lookup.

Re-implement has() with its own TTL check so cache statistics stay
accurate.

35 unit tests added including 3 regression tests for this fix.

Closes SynkraAI#497
Copilot AI review requested due to automatic review settings February 24, 2026 20:42
@vercel
Copy link

vercel bot commented Feb 24, 2026

@nikolasdehor is attempting to deploy a commit to the Pedro Valério Lopez's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Walkthrough

This PR fixes a bug in ConfigCache where the has() method inflated hit/miss statistics by delegating to get(). The method is reimplemented with inline TTL expiration checks. Additionally, a comprehensive test suite is added, and the install manifest is updated with recalculated hashes and a new timestamp.

Changes

Cohort / File(s) Summary
ConfigCache Implementation
.aios-core/core/config/config-cache.js
Reimplemented has() method to perform existence and TTL expiration checks without delegating to get(), preventing inflation of hit/miss statistics (fixes issue #497).
ConfigCache Tests
tests/core/config/config-cache.test.js
Added comprehensive Jest test suite covering constructor, get/set behavior, TTL expiration, statistics tracking, serialization, and singleton instance behavior with fake timers for expiration simulation.
Manifest Updates
.aios-core/install-manifest.yaml
Updated generated timestamp and recalculated SHA-256 hashes and file sizes across multiple manifest entries (core, data, scripts, templates, infrastructure).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

core, tests, installer

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: reimplementation of has() to avoid inflating hit/miss statistics by not delegating to get().
Linked Issues check ✅ Passed Changes directly address issue #497 by reimplementing has() with own TTL check, removing get() delegation and hit/miss counting, with comprehensive test coverage validating the fix.
Out of Scope Changes check ✅ Passed All changes are directly related to issue #497: has() fix, corresponding tests, and manifest update reflecting code changes. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes ConfigCache.has() so it no longer inflates hit/miss counters by avoiding delegation to get(), and adds regression coverage to prevent the double-counting pattern.

Changes:

  • Reimplemented ConfigCache.has() to check presence/TTL directly without mutating statistics.
  • Added comprehensive unit tests (including regression tests for #497) covering cache behavior and stats.
  • Updated install manifest metadata/hashes to reflect the modified core file(s).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
.aios-core/core/config/config-cache.js Updates has() to avoid incrementing cache hit/miss stats while still enforcing TTL + cleanup.
tests/core/config/config-cache.test.js Adds unit + regression tests to validate TTL behavior and ensure has() does not affect stats.
.aios-core/install-manifest.yaml Updates generated manifest timestamp and file hashes/sizes for changed artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +77 to +85
if (!this.cache.has(key)) return false;

const timestamp = this.timestamps.get(key);
if (Date.now() - timestamp > this.ttl) {
this.cache.delete(key);
this.timestamps.delete(key);
return false;
}
return true;
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

has() only checks this.cache.has(key) but assumes a corresponding timestamp exists. If the maps ever become out-of-sync (e.g., timestamp missing), Date.now() - timestamp becomes NaN and the entry will be treated as non-expired, returning true. Consider treating a missing timestamp as invalid (cleanup + false) or checking this.timestamps.has(key) alongside this.cache.has(key).

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +85
// Check directly instead of delegating to get(), which would
// increment hits/misses and inflate cache statistics (fixes #497)
if (!this.cache.has(key)) return false;

const timestamp = this.timestamps.get(key);
if (Date.now() - timestamp > this.ttl) {
this.cache.delete(key);
this.timestamps.delete(key);
return false;
}
return true;
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

This duplicates expiration/cleanup logic that likely also exists in get(), which can drift over time (e.g., boundary conditions like > vs >=, cleanup behavior, or time source). A concrete way to prevent divergence is to extract a shared internal helper that validates/cleans a key without touching stats, and have both get() and has() rely on it.

Copilot uses AI. Check for mistakes.
* Fixes #497 — has() was inflating hit/miss stats by calling get()
*/

jest.useFakeTimers();
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

The suite enables fake timers at module scope but never restores real timers. To reduce cross-suite coupling (especially if this file is ever refactored/merged), add an afterAll(() => jest.useRealTimers()) (or equivalent) in this test file.

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/core/config/config-cache.test.js (1)

12-12: Restore real timers after the suite.

jest.useFakeTimers() is module-scoped with no corresponding teardown. If the suite ever runs --runInBand (or in a shared worker), the fake timer state can leak into subsequently loaded modules. A single afterAll guard is cheap insurance.

🛡️ Proposed fix
 jest.useFakeTimers();
 
 const { ConfigCache, globalConfigCache } = require('../../../.aios-core/core/config/config-cache');
+
+afterAll(() => {
+  jest.useRealTimers();
+});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/config/config-cache.test.js` at line 12, Add a teardown to restore
real timers after the suite: the test file calls jest.useFakeTimers() but never
reverts the timer implementation, so add an afterAll hook that calls
jest.useRealTimers() (i.e., include an afterAll(() => jest.useRealTimers()) near
the top-level of the test file) to prevent fake timer state leaking across
modules or when running with --runInBand.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/core/config/config-cache.test.js`:
- Line 12: Add a teardown to restore real timers after the suite: the test file
calls jest.useFakeTimers() but never reverts the timer implementation, so add an
afterAll hook that calls jest.useRealTimers() (i.e., include an afterAll(() =>
jest.useRealTimers()) near the top-level of the test file) to prevent fake timer
state leaking across modules or when running with --runInBand.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9425a35 and f9fa146.

📒 Files selected for processing (3)
  • .aios-core/core/config/config-cache.js
  • .aios-core/install-manifest.yaml
  • tests/core/config/config-cache.test.js

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.

bug: ConfigCache.has() inflates hit/miss stats by calling get() internally

2 participants