Skip to content

fix(AUDIT-M2): prune rate-limit timestamp arrays in record* methods#285

Merged
xlabtg merged 3 commits intoxlabtg:mainfrom
konard:issue-284-f17c9ac09606
Apr 22, 2026
Merged

fix(AUDIT-M2): prune rate-limit timestamp arrays in record* methods#285
xlabtg merged 3 commits intoxlabtg:mainfrom
konard:issue-284-f17c9ac09606

Conversation

@konard
Copy link
Copy Markdown

@konard konard commented Apr 22, 2026

Summary

Fixes AUDIT-M2: recordToolCall() and recordApiCall() in PolicyEngine pushed timestamps into arrays without ever pruning them between checkAction() calls, causing unbounded memory growth proportional to call count.

Root cause: Timestamp pruning only happened inside checkAction(). Any code path that called recordToolCall() / recordApiCall() without a subsequent checkAction() would accumulate entries forever.

Fix: Both record methods now prune expired entries immediately on every call using the same window as checkAction() (3 600 000 ms for tool calls, 60 000 ms for API calls), and additionally cap the array to the configured rate-limit maximum via .slice(). This keeps memory proportional to the configured cap regardless of call frequency.

Changes

  • src/autonomous/policy-engine.ts — add filter + slice in recordToolCall and recordApiCall
  • src/autonomous/__tests__/policy-engine.test.ts — add two regression tests per acceptance criteria in the issue

How to reproduce the issue

const engine = new PolicyEngine(DEFAULT_POLICY_CONFIG);
for (let i = 0; i < 10_000; i++) engine.recordToolCall();
console.log(engine.serialize().toolCallTimestamps.length); // was 10 000, now ≤ 100

Test plan

  • New test: toolCallTimestamps stays bounded after many recordToolCall calls without checkAction — asserts array length ≤ toolCallsPerHour (100) after 10 000 calls
  • New test: apiCallTimestamps stays bounded after many recordApiCall calls without checkAction — asserts array length ≤ apiCallsPerMinute (30) after 10 000 calls
  • All 20 existing + new policy-engine tests pass
  • All 118 autonomous suite tests pass

Fixes #284

konard added 2 commits April 22, 2026 21:13
Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: xlabtg#284
recordToolCall() and recordApiCall() previously pushed timestamps without
bounds, growing the arrays indefinitely between checkAction() calls. Fix by
filtering out expired entries and slicing to the rate-limit cap on every
record call, keeping memory proportional to the configured limit regardless
of how many calls occur before the next policy check.

Adds two unit tests verifying that 10 000 consecutive calls without a
checkAction() leave the arrays no larger than the configured rate-limit cap.

Closes xlabtg#284
@konard konard changed the title [WIP] audit-m2-rate-limit-timestamps-unbounded fix(AUDIT-M2): prune rate-limit timestamp arrays in record* methods Apr 22, 2026
@konard konard marked this pull request as ready for review April 22, 2026 21:17
@konard
Copy link
Copy Markdown
Author

konard commented Apr 22, 2026

Solution summary

Done. Here's a summary of what was done:

Root cause: recordToolCall() and recordApiCall() in src/autonomous/policy-engine.ts appended timestamps unconditionally. Pruning only happened inside checkAction(), so arrays grew without bound between policy checks.

Fix (policy-engine.ts:234-251): Each record method now:

  1. Filters out entries outside the sliding window (1 hour / 1 minute)
  2. Slices to the configured rate-limit cap (toolCallsPerHour / apiCallsPerMinute)

This guarantees the arrays stay bounded at all times, not just at check time.

Tests added: Two new cases in policy-engine.test.ts verify that 10,000 consecutive record* calls leave the arrays at ≤ 100 and ≤ 30 entries respectively — matching the acceptance criteria from the issue.

PR: #285


This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Author

konard commented Apr 22, 2026

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $0.618400

📊 Context and tokens usage:

  • 46.0K / 1M (5%) input tokens, 6.7K / 64K (11%) output tokens

Total: (38.6K + 1.2M cached) input tokens, 6.7K output tokens, $0.618400 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: sonnet
  • Model: Claude Sonnet 4.6 (claude-sonnet-4-6)

📎 Log file uploaded as Gist (952KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Author

konard commented Apr 22, 2026

🔄 Auto-restart triggered (iteration 1)

Reason: Merge conflicts detected

Starting new session to address the issues.


Auto-restart-until-mergeable mode is active. Will continue until PR becomes mergeable.

@xlabtg xlabtg merged commit 9219112 into xlabtg:main Apr 22, 2026
@konard
Copy link
Copy Markdown
Author

konard commented Apr 22, 2026

🔄 Auto-restart-until-mergeable Log (iteration 1)

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $0.193295

📊 Context and tokens usage:

  • 23.4K / 1M (2%) input tokens, 2.3K / 64K (4%) output tokens

Total: (13.5K + 362.2K cached) input tokens, 2.3K output tokens, $0.193295 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: sonnet
  • Model: Claude Sonnet 4.6 (claude-sonnet-4-6)

📎 Log file uploaded as Gist (1370KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

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.

audit-m2-rate-limit-timestamps-unbounded

2 participants