Skip to content

fix(performance): fit trends against time and treat idle caches as no data - #274

Merged
docdyhr merged 3 commits into
mainfrom
fix/performance-review-followups
Oct 12, 2026
Merged

docdyhr merged 3 commits into
mainfrom
fix/performance-review-followups

Conversation

@docdyhr

@docdyhr docdyhr commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Pull Request

Description

Follow-ups from the review of #270 and #272. History is live now, so code that never received data before is running for the first time.

  • Trends regressed over the sample index, but snapshots are unevenly spaced: the analytics timer and every wp_performance_history call add one. "One sample ahead" was labelled a 30-minute prediction, and predictPerformance(timeframeMinutes) ignored its horizon, although its alerts say "in N minutes". The direction threshold was a fixed slope of 0.1, which meant 0.1 ms per sample for response time but 10 percentage points for the hit rate.
  • Idle caches. The contract says a cache with no lookups is neutral, not 0%, but:
    • the hit-rate trend still counted idle points;
    • history's averageCacheHitRate averaged them in;
    • wp_cache_stats, wp_cache_info and wp_performance_stats printed "0.0%".
  • Window edges. Pruning used >, while getHistoricalData(start) used >=.
  • Scope labels didn't say history/export chart series are sampled to at most 100 points. downsample(items, 0) returned one item, and the supported timeframes were a second hand-kept list next to the parseTimeframe map.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)

Changes Made

  • Trends:
    • fit against timestamp, falling back to the index only when all points share one timestamp;
    • predict prediction.timeframe past the newest point (30 min, or predictPerformance's horizon);
    • report a direction only when the fitted change across the points exceeds the metric's noise floor (the one anomaly detection uses).
  • Idle caches:
    • left out of the hit-rate trend and of averageCacheHitRate (null if no snapshot had lookups);
    • shown as "n/a (no lookups yet)" via the new formatHitRate().
  • Inclusive window edges in monitor pruning, analytics pruning and anomaly expiry.
  • Scope labels mention sampling; downsample(items, 0) returns []; SUPPORTED_TIMEFRAMES is derived from the map.
  • DOX src/performance/AGENTS.md and docs/PERFORMANCE_MONITORING.md updated.

Related Issues

Related to #270 and #272 (review follow-ups).

Testing

Tests Performed

  • Unit tests pass (npm test): 2778 passed before, 2796 passed after, 0 failed
  • build, typecheck, lint, format:check, security:scan
  • RED first: on main, the 10 new behavioural tests and the 3 extended export-label cases failed. 8 guard cases pass both ways: downsample within budget and keeping both ends, and every timeframe being known.

Test Cases

  1. An uneven series rising 10 ms/min (minutes 0–3, then 60) predicts 1000 ms at +30 min, and predictPerformance(60) predicts 1300.
  2. Idle → warm cache points give a stable hit-rate trend, not improving.
  3. A snapshot exactly one retention period old is kept and returned by a 24 h request.
  4. averageCacheHitRate = 0.85 over (idle, 0.8, 0.9), and null for idle-only history; hit_rate shows n/a (no lookups yet).
  5. Scope labels: sampling is mentioned for history and export.
  6. downsample edge cases; every parseTimeframe key is a supported timeframe.

One mid-fix finding: a first version based "direction" on the prediction horizon. That broke an existing test, rightly so: a steady memory climb from 60% to 96% read as stable. Direction now describes the observed span, so the same history can't change direction with the horizon.

Breaking Changes

None in shape, apart from two narrowed values: averageCacheHitRate can be null, and hit-rate strings can read n/a (no lookups yet) before the first cache lookup.

Documentation

  • Documentation updated
  • CHANGELOG.md (semantic-release writes it)

🤖 Generated with Claude Code

Summary by Sourcery

Fix performance analytics to model uneven history accurately and represent idle caches as having no data.

Bug Fixes:

  • Correct trend analysis and predictions for unevenly spaced snapshots by fitting against timestamps and honoring requested prediction horizons.
  • Treat cache snapshots with no lookups as unavailable rather than 0% across trends, averages, cache output, and efficiency reporting.
  • Make retention and analytics windows inclusive at their boundaries.

Enhancements:

  • Align trend direction thresholds with metric-specific anomaly noise floors and centralize supported timeframe definitions.
  • Limit history and export chart series to 100 sampled points and handle zero-size downsampling explicitly.

Documentation:

  • Document timestamp-based trend analysis, prediction horizons, idle-cache handling, and history/export sampling behavior.

Tests:

  • Add coverage for uneven trend sampling, prediction horizons, idle-cache exclusion, inclusive retention boundaries, output formatting, sampling edge cases, and timeframe consistency.

… data

Follow-ups from the review of #270/#272, now that history is live:

- Trends regressed over the sample index, but snapshots are unevenly
  spaced (the analytics timer and every wp_performance_history call add
  one), and "one sample ahead" was labelled a 30-minute prediction;
  predictPerformance(timeframeMinutes) ignored its horizon. Trends now
  fit against timestamps and predict prediction.timeframe past the
  newest point; a direction needs a fitted change above the metric's
  noise floor (the fixed 0.1 slope meant ms for one metric and 10
  percentage points for another).
- A cache with no lookups is neutral, not 0%: idle points are left out
  of the hit-rate trend and of history's averageCacheHitRate (null if
  none had lookups), and hit-rate strings read "n/a (no lookups yet)"
  in wp_cache_stats, wp_cache_info and wp_performance_stats.
- History windows are inclusive at both ends: pruning used ">" while
  getHistoricalData(start) used ">=".
- History/export scope labels say the chart series are sampled to at
  most 100 points; downsample(items, 0) returns no items; the supported
  timeframes come from the parseTimeframe map instead of a second list.

Tests: 18 new; suite 2778 passed before, 2796 after, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 11, 2026 16:43
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T16:46:51.250278Z 062b533 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@sourcery-ai

sourcery-ai Bot commented Oct 11, 2026

Copy link
Copy Markdown

Sorry @docdyhr, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 3 days and 18 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@github-actions github-actions Bot added the docs Documentation changes label Oct 11, 2026
@sourcery-ai

sourcery-ai Bot commented Oct 11, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates performance analytics to use timestamp-based trend fitting and horizon-aware predictions, treats idle caches as having no hit-rate data, makes retention boundaries inclusive, and clarifies/enforces chart sampling and timeframe metadata across tools and documentation.

Sequence diagram for timestamp-based performance trend prediction

sequenceDiagram
    participant Caller
    participant PerformanceAnalytics
    participant TrendAnalysis

    Caller->>PerformanceAnalytics: predictPerformance(timeframeMinutes)
    PerformanceAnalytics->>TrendAnalysis: analyzeTrends(timeframeMinutes * 60 * 1000)
    TrendAnalysis->>TrendAnalysis: Fit points using timestamp
    TrendAnalysis->>TrendAnalysis: Exclude idle cache points from cacheHitRate
    TrendAnalysis->>TrendAnalysis: Compare observed change with anomalyNoiseFloor
    TrendAnalysis-->>PerformanceAnalytics: PerformanceTrend with prediction.timeframe
    PerformanceAnalytics-->>Caller: Predictions and alerts for requested horizon
Loading

Flow diagram for idle cache hit-rate handling

flowchart LR
    Snapshot[Cache snapshot] --> Activity{hasCacheActivity}
    Activity -->|No lookups| NoData[No hit-rate data]
    Activity -->|Has lookups| Rate[Use hit rate]
    NoData --> Format[formatHitRate]
    Rate --> Format
    Format --> Output["Percentage or n/a (no lookups yet)"]
    NoData --> Average[Exclude from averageCacheHitRate]
    Rate --> Average
    Average --> Summary[Fraction or null]
Loading

Flow diagram for sampled historical chart output

flowchart LR
    History[Historical snapshots] --> Metrics[All-snapshot metrics and summary]
    History --> Downsample["downsample(items, MAX_HISTORY_POINTS)"]
    Downsample --> Chart[Chart series at most 100 points]
    Metrics --> Counts[dataPoints and historicalDataPoints count all snapshots]
Loading

File-Level Changes

Change Details Files
Make trend regression and prediction time-aware while using metric-specific noise floors for direction classification.
  • Fit unevenly spaced points against timestamps, with an index fallback when timestamps have no spread.
  • Project predictions 30 minutes beyond the latest point, or across the requested predictPerformance horizon.
  • Exclude idle cache points from hit-rate trends and base direction on observed fitted change rather than prediction horizon.
src/performance/PerformanceAnalytics.ts
tests/performance/HistoryWindow.test.js
docs/PERFORMANCE_MONITORING.md
src/performance/AGENTS.md
Represent caches with no lookups as missing hit-rate data throughout analytics, history, and tool output.
  • Exclude idle snapshots from average cache hit rate and return null when all snapshots are idle.
  • Centralize hit-rate formatting as either a percentage or n/a (no lookups yet).
  • Update cache and performance tools plus behavioral tests for idle-cache output.
src/tools/performance/PerformanceHelpers.ts
src/tools/cache.ts
src/tools/performance/PerformanceTools.ts
tests/tools/cache.test.js
tests/performance/OutputScope.test.js
tests/tools/performance/PerformanceHelpers.test.js
docs/PERFORMANCE_MONITORING.md
Align retention and anomaly windows with inclusive historical-data selection.
  • Retain and return snapshots exactly at the cutoff timestamp.
  • Keep anomalies exactly at the lookback boundary.
src/performance/PerformanceMonitor.ts
src/performance/PerformanceAnalytics.ts
tests/performance/HistoryWindow.test.js
Make history/export sampling and timeframe metadata consistent and enforce the zero-budget downsampling edge case.
  • Derive supported timeframes from the parsing map.
  • Return no items when downsampling with a zero maximum.
  • Document that chart series are capped at 100 points while snapshot counts remain unsampled.
src/tools/performance/PerformanceHelpers.ts
src/tools/performance/PerformanceTools.ts
tests/tools/performance/PerformanceHelpers.test.js
tests/performance/OutputScope.test.js
docs/PERFORMANCE_MONITORING.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The implementation consistently addresses the described analytics defects with focused regression coverage and matching documentation.

0 open findings

What changed in this PR

Corrects performance analytics for uneven sampling, idle caches, and inclusive history boundaries.

Changes:

  • Regresses trends by timestamp and honors prediction horizons.
  • Treats idle-cache hit rates as unavailable.
  • Aligns retention boundaries, sampling labels, tests, and documentation.
File Description
src/​performance/​PerformanceAnalytics.ts Adds time-based trends, horizon-aware predictions, and inclusive expiry.
src/​performance/​PerformanceMonitor.ts Makes retention boundaries inclusive.
src/​tools/​performance/​PerformanceHelpers.ts Adds hit-rate formatting and improves timeframe/downsampling logic.
src/​tools/​performance/​PerformanceTools.ts Applies idle-cache semantics and sampling labels.
src/​tools/​cache.ts Formats inactive cache hit rates as unavailable.
src/​performance/​AGENTS.md Documents performance analytics contracts.
docs/​PERFORMANCE_MONITORING.md Updates user-facing behavior documentation.
tests/​performance/​HistoryWindow.test.js Tests boundaries, trends, horizons, and idle caches.
tests/​performance/​OutputScope.test.js Tests summaries and sampling scope labels.
tests/​tools/​cache.test.js Tests idle-cache output.
tests/​tools/​performance/​PerformanceHelpers.test.js Tests helper edge cases and formatting.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 062b5330c1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/tools/performance/PerformanceTools.ts
docdyhr and others added 2 commits October 11, 2026 18:58
Review follow-up (Codex on #274): wp_performance_stats showed the hit
rate as "n/a (no lookups yet)" next to efficiency "Poor", because
calculateCacheEfficiency() scored the undefined 0% hit rate.
calculateCacheEfficiency() now returns "n/a (no lookups yet)" when the
cache has had no lookups, like formatHitRate().

Tests: 1 new (failed before the fix); suite 2797 passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@docdyhr
docdyhr merged commit 2996b29 into main Oct 12, 2026
24 checks passed
@docdyhr
docdyhr deleted the fix/performance-review-followups branch October 12, 2026 01:24
docdyhr pushed a commit that referenced this pull request Oct 12, 2026
## [4.1.6](v4.1.5...v4.1.6) (2026-10-12)

### 🐛 Bug Fixes

* **performance:** fit trends against time and treat idle caches as no data ([#274](#274)) ([2996b29](2996b29)), closes [270/#272](https://github.com/270/mcp-wordpress/issues/272)
@docdyhr

docdyhr commented Oct 12, 2026

Copy link
Copy Markdown
Owner Author

🎉 This PR is included in version 4.1.6 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Documentation changes released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants