Skip to content

Add scan-to-scan accessibility regression detection and sidebar trend messaging#1588

Open
SteveJonesDev wants to merge 2 commits into
developfrom
codex/implement-regression-detection-feature
Open

Add scan-to-scan accessibility regression detection and sidebar trend messaging#1588
SteveJonesDev wants to merge 2 commits into
developfrom
codex/implement-regression-detection-feature

Conversation

@SteveJonesDev
Copy link
Copy Markdown
Member

@SteveJonesDev SteveJonesDev commented Mar 20, 2026

Motivation

  • Provide an early-warning/regression feature so users can see when accessibility is declining between scans and understand the risk of stopping ongoing checks.
  • Make the sidebar UI actionable by replacing placeholder trend text with concrete, scan-to-scan messages and a retention-focused callout about what will break if scanning stops.

Description

  • Added scan-to-scan regression detection in Summary_Generator by comparing the current scan summary with the previously saved summary and producing a regression payload containing status, delta, has_baseline, and scanned_at via build_regression_data (new private method) and added regression into the sanitized summary output in sanitize_summary_meta_data (file: includes/classes/class-summary-generator.php).
  • Persisted a compact scan history in post meta (_edac_summary_history) for up to 10 scans to support trend messaging via update_summary_history (new private method) and continue writing the primary _edac_summary meta (file: includes/classes/class-summary-generator.php).
  • Ensured the REST sidebar summary payload always contains a regression object (defaults provided) so older summaries and UI consumers remain compatible (file: includes/classes/class-rest-api.php).
  • Replaced sidebar placeholders with dynamic microcopy that shows regression/improvement/no-change statements for Problems, Needs Review, and Passed Checks, added a retention-focused message when status is declining, and added styles to support the new microcopy (files: src/sidebar/components/Panels/AccessibilityStatus.js, src/sidebar/sass/components/accessibility-status.scss).
  • Added/updated PHPUnit unit tests to cover regression defaults and the regression classification logic (files: tests/phpunit/includes/classes/RestApiSidebarDataTest.php, tests/phpunit/includes/classes/SummaryGeneratorTest.php).
  • Bumped package-lock.json version metadata to match package version.

Testing

  • Ran JavaScript linting for the modified sidebar component with npm run lint:js -- src/sidebar/components/Panels/AccessibilityStatus.js, which completed successfully.
  • Checked PHP syntax with php -l on modified PHP files: includes/classes/class-summary-generator.php, includes/classes/class-rest-api.php, and related test files, which returned no syntax errors.
  • Attempted to run the PHPUnit tests with vendor/bin/phpunit tests/phpunit/includes/classes/SummaryGeneratorTest.php tests/phpunit/includes/classes/RestApiSidebarDataTest.php, however the environment is missing the WordPress test library (/tmp/wordpress-tests-lib/includes/functions.php) so full test execution could not be completed in this run (the unit test assertions were added and verify expected behavior when run in CI or a properly provisioned local test environment).

Codex Task

Summary by CodeRabbit

  • New Features

    • Always-show regression summary with baseline detection and per-metric deltas
    • Historical scan retention (compact entries, up to 10) for trend tracking
    • Accessibility panel now displays regression messages and a retention/drift note
  • Style

    • Added styles for trend text and retention message layout
  • Tests

    • Updated expectations and added a test validating declining regression behavior

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the accessibility checker by introducing scan-to-scan regression detection and dynamic trend messaging in the sidebar. The core purpose is to provide users with immediate, actionable feedback on how accessibility is evolving over time, highlighting declines and encouraging continuous monitoring. This is achieved by comparing current scan results with previous ones, storing a history of summaries, and updating the user interface to display insightful trends rather than static placeholders.

Highlights

  • Accessibility Regression Detection: Implemented a new feature to detect and report accessibility regressions between scans, providing early warnings based on changes in errors, warnings, and passed tests.
  • Sidebar Trend Messaging: Enhanced the sidebar UI with dynamic messages indicating accessibility trends (regression, improvement, no change) for problems, warnings, and passed checks, along with a retention-focused callout.
  • Scan History Persistence: Introduced a mechanism to store a compact history of up to 10 previous scan summaries in post meta, enabling trend analysis.
  • API Compatibility: Ensured the REST API consistently provides a default 'regression' object in the summary payload, maintaining compatibility with older summaries and UI consumers.
  • Unit Test Coverage: Expanded PHPUnit tests to cover the new regression detection logic and API data structures, including a test for declining status classification.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 20, 2026

📝 Walkthrough

Walkthrough

Adds regression tracking: backend computes per-scan deltas, status, and history; REST API always returns a regression structure with defaults; frontend reads and displays regression messages and a retention/drift row; tests updated to cover regression behavior.

Changes

Cohort / File(s) Summary
Backend: Summary generation & storage
includes/classes/class-summary-generator.php
Added regression handling in generate_summary(), new private helpers issue_counts_match() and build_regression_data(), persisted regression into _edac_summary, updated save_summary_meta_data() to call update_summary_history() and truncate history to last 10 entries, and extended sanitize_summary_meta_data() to sanitize regression/delta/scanned_at.
Backend: REST output
includes/classes/class-rest-api.php
Updated REST_Api::get_summary_data() to ensure returned summary always contains a regression structure with sensible defaults when meta is missing or malformed.
Frontend: Sidebar display
src/sidebar/components/Panels/AccessibilityStatus.js
Reads summary.regression and delta; adds renderRegressionMessage() to render baseline/magnitude/direction messages; replaces placeholder trend text in several cards and conditionally shows retention/drift PanelRow when status is declining.
Styling
src/sidebar/sass/components/accessibility-status.scss
Added .edac-status-card__meta--trend rules and .edac-status-retention-message block; small cleanup of trailing brace.
Tests
tests/phpunit/includes/classes/RestApiSidebarDataTest.php, tests/phpunit/includes/classes/SummaryGeneratorTest.php
Updated REST API default assertions to include regression; loosened some meta equality assertions and added test_build_regression_data_marks_declining_status() verifying delta computation and declining status via reflection.

Sequence Diagram(s)

sequenceDiagram
    participant Scan as Accessibility Scan
    participant Gen as Summary Generator
    participant Meta as Post Meta Storage
    participant REST as REST API
    participant UI as Sidebar UI

    Scan->>Gen: provide current metrics
    Gen->>Meta: read `_edac_summary` (previous)
    Gen->>Gen: issue_counts_match(previous, current)?
    alt counts match & previous regression exists
        Gen->>Gen: reuse previous regression
    else
        Gen->>Gen: build_regression_data(previous, current)
    end
    Gen->>Meta: save `_edac_summary` (includes regression)
    Gen->>Meta: update `_edac_summary_history` (append, dedupe, truncate to 10)

    UI->>REST: GET summary
    REST->>Meta: fetch `_edac_summary`
    REST->>REST: ensure `regression` present (defaults if missing)
    REST-->>UI: return summary with `regression`

    UI->>UI: renderRegressionMessage(delta, status)
    UI-->>User: display trend/regression messages and retention row if declining
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • pattonwebz

Poem

🐰 I hop through scans both new and old,

Counting deltas brave and bold,
Baselines kept in tidy rows,
Trends I show where insight grows,
A carrot for each score that rose.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary changes: adding scan-to-scan regression detection and sidebar trend messaging, which aligns with the main objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/implement-regression-detection-feature

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: 0638fd84a1

ℹ️ 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 on lines +117 to +121
if ( $regression_score >= 5 || $deltas['passed_tests'] <= -10 ) {
$status = 'declining';
} elseif ( $regression_score > 0 || $deltas['passed_tests'] < 0 ) {
$status = 'watch';
} elseif ( $deltas['errors'] < 0 || $deltas['warnings'] < 0 || $deltas['contrast_errors'] < 0 || $deltas['passed_tests'] > 0 ) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate regression status on baseline availability

This classification runs even when there is no prior summary, so a first-ever scan with existing errors/warnings is labeled watch/declining despite has_baseline being false. The sidebar then uses that status for retention messaging, which can incorrectly tell users accessibility is declining before any scan-to-scan comparison exists. Please short-circuit to stable (or equivalent neutral status) until a baseline is present.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new accessibility regression tracking feature. The backend changes involve modifying the REST API to include a new regression data structure in the summary, which contains details like has_baseline, status, delta for errors, warnings, contrast errors, and passed tests, along with a scanned_at timestamp. A new build_regression_data method is added to calculate these deltas and determine a regression status (stable, declining, watch, improving) by comparing the current scan with the previous one. Additionally, a update_summary_history method is implemented to store a compact history of the last 10 summaries for trend analysis, and the sanitization process is updated for the new regression data. On the frontend, the AccessibilityStatus component is updated to display these new regression metrics, showing trend messages for problems, warnings, and passed checks, and a retention message based on the overall regression status. Corresponding SCSS styles are added for the new UI elements. Unit tests have been updated and added to cover the initialization and calculation of the regression data.

Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
includes/classes/class-rest-api.php (1)

877-900: LGTM! Consider extracting the default regression structure to reduce duplication.

The logic correctly ensures backward compatibility by always providing a regression object. The two code paths (lines 877-888 and 890-900) use identical default structures.

,

♻️ Optional: Extract default structure to reduce duplication
 private function get_summary_data( $post_id ) {
 	// Get summary from post meta.
 	$summary = get_post_meta( $post_id, '_edac_summary', true );
+
+	$default_regression = [
+		'has_baseline' => false,
+		'status'       => 'stable',
+		'delta'        => [
+			'errors'          => 0,
+			'warnings'        => 0,
+			'contrast_errors' => 0,
+			'passed_tests'    => 0,
+		],
+		'scanned_at'   => '',
+	];
 
 	// If summary doesn't exist or is invalid, return defaults.
 	if ( ! $summary || ! is_array( $summary ) ) {
 		$summary = [
 			'passed_tests'    => 0,
 			'errors'          => 0,
 			'contrast_errors' => 0,
 			'warnings'        => 0,
 			'ignored'         => 0,
 			'readability'     => 0,
-			'regression'      => [
-				'has_baseline' => false,
-				'status'       => 'stable',
-				'delta'        => [
-					'errors'          => 0,
-					'warnings'        => 0,
-					'contrast_errors' => 0,
-					'passed_tests'    => 0,
-				],
-				'scanned_at'   => '',
-			],
+			'regression'      => $default_regression,
 		];
 	} elseif ( ! isset( $summary['regression'] ) || ! is_array( $summary['regression'] ) ) {
-		$summary['regression'] = [
-			'has_baseline' => false,
-			'status'       => 'stable',
-			'delta'        => [
-				'errors'          => 0,
-				'warnings'        => 0,
-				'contrast_errors' => 0,
-				'passed_tests'    => 0,
-			],
-			'scanned_at'   => '',
-		];
+		$summary['regression'] = $default_regression;
 	}
 
 	return $summary;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@includes/classes/class-rest-api.php` around lines 877 - 900, Extract the
duplicated default regression array into a single reusable symbol (e.g., a
private method get_default_regression() or a private const DEFAULT_REGRESSION)
and replace both inline arrays that set $summary['regression'] with a
call/reference to that symbol; ensure the new symbol returns the same structure
(has_baseline, status, delta with errors/warnings/contrast_errors/passed_tests,
scanned_at) so both the initial assignment and the elseif branch use the
centralized default.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@includes/classes/class-summary-generator.php`:
- Around line 109-110: Remove the manual alignment of the assignment operators
causing the PHPCS warning by normalizing spacing for the two assignments: change
the lines that set $current_value and $previous_value (which reference
$current_summary, $previous_summary and call absint()) so they use a single
space around the = operator instead of padded alignment; keep the same logic
(use the null-coalescing/default and is_array check) but ensure spacing is
consistent with PSR/PHPCS rules.

---

Nitpick comments:
In `@includes/classes/class-rest-api.php`:
- Around line 877-900: Extract the duplicated default regression array into a
single reusable symbol (e.g., a private method get_default_regression() or a
private const DEFAULT_REGRESSION) and replace both inline arrays that set
$summary['regression'] with a call/reference to that symbol; ensure the new
symbol returns the same structure (has_baseline, status, delta with
errors/warnings/contrast_errors/passed_tests, scanned_at) so both the initial
assignment and the elseif branch use the centralized default.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 02d72660-524b-4a93-a2a3-55c5ec19e03f

📥 Commits

Reviewing files that changed from the base of the PR and between 7b6ef1f and 0638fd8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • includes/classes/class-rest-api.php
  • includes/classes/class-summary-generator.php
  • src/sidebar/components/Panels/AccessibilityStatus.js
  • src/sidebar/sass/components/accessibility-status.scss
  • tests/phpunit/includes/classes/RestApiSidebarDataTest.php
  • tests/phpunit/includes/classes/SummaryGeneratorTest.php

Comment on lines +109 to +110
$current_value = absint( $current_summary[ $metric ] ?? 0 );
$previous_value = is_array( $previous_summary ) ? absint( $previous_summary[ $metric ] ?? 0 ) : 0;
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.

⚠️ Potential issue | 🟡 Minor

Fix PHPCS alignment warning.

The pipeline flagged an alignment issue with the equals signs in these assignments.

🔧 Proposed fix
 	foreach ( $metrics as $metric ) {
-		$current_value = absint( $current_summary[ $metric ] ?? 0 );
-		$previous_value = is_array( $previous_summary ) ? absint( $previous_summary[ $metric ] ?? 0 ) : 0;
+		$current_value  = absint( $current_summary[ $metric ] ?? 0 );
+		$previous_value = is_array( $previous_summary ) ? absint( $previous_summary[ $metric ] ?? 0 ) : 0;
 		$deltas[ $metric ] = $current_value - $previous_value;
 	}
🧰 Tools
🪛 GitHub Actions: CS

[warning] 109-110: PHPCS Generic.Formatting.MultipleStatementAlignment.NotSameWarning: Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space / expected 4 spaces but found 1 space.

🪛 GitHub Check: Check code style

[warning] 110-110:
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space


[warning] 109-109:
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@includes/classes/class-summary-generator.php` around lines 109 - 110, Remove
the manual alignment of the assignment operators causing the PHPCS warning by
normalizing spacing for the two assignments: change the lines that set
$current_value and $previous_value (which reference $current_summary,
$previous_summary and call absint()) so they use a single space around the =
operator instead of padded alignment; keep the same logic (use the
null-coalescing/default and is_array check) but ensure spacing is consistent
with PSR/PHPCS rules.

…s match and include content grade in summary updates
Copy link
Copy Markdown
Contributor

@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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@includes/classes/class-summary-generator.php`:
- Around line 443-453: The current comparison uses absint(... ?? -1) which turns
a missing value into 1 and can falsely match real values; update the block that
checks $last vs $new_entry so you first ensure each metric exists (use
isset($last['errors']) / isset($new_entry['errors']) etc.) and then compare
their integer values (e.g., absint($last['errors']) ===
absint($new_entry['errors'])) only when both exist, otherwise treat as
different; apply this pattern to errors, warnings, contrast_errors, passed_tests
and content_grade to avoid the -1 fallback ambiguity.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 910f48ea-90a6-4b1a-b78c-166cecd007fa

📥 Commits

Reviewing files that changed from the base of the PR and between 0638fd8 and acc8794.

📒 Files selected for processing (2)
  • includes/classes/class-summary-generator.php
  • src/sidebar/components/Panels/AccessibilityStatus.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/sidebar/components/Panels/AccessibilityStatus.js

Comment on lines +443 to +453
if ( ! empty( $history ) ) {
$last = end( $history );
if (
absint( $last['errors'] ?? -1 ) === $new_entry['errors'] &&
absint( $last['warnings'] ?? -1 ) === $new_entry['warnings'] &&
absint( $last['contrast_errors'] ?? -1 ) === $new_entry['contrast_errors'] &&
absint( $last['passed_tests'] ?? -1 ) === $new_entry['passed_tests'] &&
absint( $last['content_grade'] ?? -1 ) === $new_entry['content_grade']
) {
return;
}
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.

⚠️ Potential issue | 🟡 Minor

Default value with absint() can cause false matches.

Using -1 as a fallback with absint() yields 1, which could incorrectly match a legitimate entry where the metric equals 1—causing that entry to be skipped.

Consider using a direct comparison without the absint() wrapper on the fallback, or restructure to avoid the ambiguity:

🛡️ Suggested fix
 		if (
-			absint( $last['errors'] ?? -1 ) === $new_entry['errors'] &&
-			absint( $last['warnings'] ?? -1 ) === $new_entry['warnings'] &&
-			absint( $last['contrast_errors'] ?? -1 ) === $new_entry['contrast_errors'] &&
-			absint( $last['passed_tests'] ?? -1 ) === $new_entry['passed_tests'] &&
-			absint( $last['content_grade'] ?? -1 ) === $new_entry['content_grade']
+			isset( $last['errors'], $last['warnings'], $last['contrast_errors'], $last['passed_tests'], $last['content_grade'] ) &&
+			absint( $last['errors'] ) === $new_entry['errors'] &&
+			absint( $last['warnings'] ) === $new_entry['warnings'] &&
+			absint( $last['contrast_errors'] ) === $new_entry['contrast_errors'] &&
+			absint( $last['passed_tests'] ) === $new_entry['passed_tests'] &&
+			absint( $last['content_grade'] ) === $new_entry['content_grade']
 		) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@includes/classes/class-summary-generator.php` around lines 443 - 453, The
current comparison uses absint(... ?? -1) which turns a missing value into 1 and
can falsely match real values; update the block that checks $last vs $new_entry
so you first ensure each metric exists (use isset($last['errors']) /
isset($new_entry['errors']) etc.) and then compare their integer values (e.g.,
absint($last['errors']) === absint($new_entry['errors'])) only when both exist,
otherwise treat as different; apply this pattern to errors, warnings,
contrast_errors, passed_tests and content_grade to avoid the -1 fallback
ambiguity.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant