Skip to content

JS-1766 Filter peach issue history to SonarJS languages#7068

Draft
francois-mora-sonarsource wants to merge 15 commits into
masterfrom
fix/skill-peach-check
Draft

JS-1766 Filter peach issue history to SonarJS languages#7068
francois-mora-sonarsource wants to merge 15 commits into
masterfrom
fix/skill-peach-check

Conversation

@francois-mora-sonarsource
Copy link
Copy Markdown
Contributor

@francois-mora-sonarsource francois-mora-sonarsource commented May 20, 2026

What changed

  • switch peach-issue-history from whole-project violations history to issue counts reconstructed from Peach project analyses plus api/issues/search
  • filter the reconstructed counts to SonarJS-owned languages: js, ts, css, web, and yaml
  • update the peach-check instructions and tests to match the new analysis-consistency behavior

Why

The previous consistency check looked at whole-project violations history, which can include non-SonarJS analyzers and produce false drop signals. This follow-up keeps the check scoped to SonarJS-owned languages.

Impact

Peach main-analysis triage should stop treating analyzer-external issue shifts as suspicious SonarJS drops.

Validation

  • node --test .claude/skills/peach-check/peach-issue-history.test.js

Summary by Gitar

  • Documentation enhancements:
    • Added Common green-path command sequence to INSTRUCTIONS.md covering the full triage workflow.
    • Clarified manual execution steps for peach-issue-history.js and added warnings about stale output files.
  • Metadata and tooling:
    • Registered peach-run-jobs.js as an official canonical helper in SKILL.md.
    • Added rm -f tool permission to SKILL.md for clean workspace management during check execution.

This will update automatically on new commits.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 20, 2026

Agentic Analysis: Early Results

Agentic Analysis and Context Augmentation are available on your project. Here are some issues that could have been prevented. Follow the links to learn how to put them into action.

86 issue(s) found across 5 file(s):

Rule File Line Message
javascript:S109 .claude/skills/peach-check/peach-drop-forensics.js 288 No magic number: 5.
javascript:S7785 .claude/skills/peach-check/peach-drop-forensics.js 309 Prefer top-level await over using a promise chain.
javascript:S5443 .claude/skills/peach-check/peach-drop-forensics.test.js 54 Make sure publicly writable directories are used safely here.
javascript:S5443 .claude/skills/peach-check/peach-drop-forensics.test.js 55 Make sure publicly writable directories are used safely here.
javascript:S5443 .claude/skills/peach-check/peach-drop-forensics.test.js 61 Make sure publicly writable directories are used safely here.
javascript:S5443 .claude/skills/peach-check/peach-drop-forensics.test.js 62 Make sure publicly writable directories are used safely here.
javascript:S5443 .claude/skills/peach-check/peach-drop-forensics.test.js 87 Make sure publicly writable directories are used safely here.
javascript:S5443 .claude/skills/peach-check/peach-drop-forensics.test.js 100 Make sure publicly writable directories are used safely here.
javascript:S5443 .claude/skills/peach-check/peach-drop-forensics.test.js 104 Make sure publicly writable directories are used safely here.
javascript:S7765 .claude/skills/peach-check/peach-issue-history.js 335 Use .includes() instead of .some() when checking value existence.
javascript:S109 .claude/skills/peach-check/peach-issue-history.js 636 No magic number: 2000.
javascript:S109 .claude/skills/peach-check/peach-issue-history.js 1023 No magic number: 404.
javascript:S3776 .claude/skills/peach-check/peach-issue-history.test.js 179 Refactor this function to reduce its Cognitive Complexity from 41 to the 15 allowed.
javascript:S134 .claude/skills/peach-check/peach-issue-history.test.js 262 Refactor this code to not nest more than 3 if/for/while/switch/try statements.
javascript:S134 .claude/skills/peach-check/peach-issue-history.test.js 265 Refactor this code to not nest more than 3 if/for/while/switch/try statements.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 295 No magic number: 400.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 437 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 441 No magic number: 531.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 455 No magic number: 5.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 461 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 495 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 498 No magic number: 7.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 513 No magic number: 7.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 519 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 522 No magic number: 9.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 523 No magic number: 5.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 541 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 544 No magic number: 79.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 545 No magic number: 21.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 564 No magic number: 5.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 570 No magic number: 50.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 570 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 587 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 594 No magic number: 50.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 609 No magic number: 50.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 614 No magic number: 131.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 617 No magic number: 600.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 631 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 639 No magic number: 600.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 640 No magic number: 600.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 644 No magic number: 6.
javascript:S7755 .claude/skills/peach-check/peach-issue-history.test.js 657 Prefer .at(…) over [….length - index].
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 662 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 664 No magic number: 5001.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 665 No magic number: 5009.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 700 No magic number: 10010.
javascript:S7770 .claude/skills/peach-check/peach-issue-history.test.js 701 arrow function is equivalent to Boolean. Use Boolean directly.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 716 No magic number: 6.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 720 No magic number: 2500.
javascript:S109 .claude/skills/peach-check/peach-issue-history.test.js 721 No magic number: 2500.

Showing 50 of 86 issues.

Analyzed by SonarQube Agentic Analysis in 3.7 s

@hashicorp-vault-sonar-prod hashicorp-vault-sonar-prod Bot changed the title Filter peach issue history to SonarJS languages JS-1766 Filter peach issue history to SonarJS languages May 20, 2026
@hashicorp-vault-sonar-prod
Copy link
Copy Markdown

hashicorp-vault-sonar-prod Bot commented May 20, 2026

JS-1766

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

Ruling Report

Code no longer flagged (1 issue)

S4782

vitest/packages/runner/src/types/tasks.ts:1365

  1363 |   /** Original file system path to the screenshot, before attachment resolution */
  1364 |   originalPath: string
> 1365 |   body?: undefined
  1366 | }
  1367 | 

@francois-mora-sonarsource francois-mora-sonarsource marked this pull request as ready for review May 20, 2026 11:45
@sonar-review-alpha
Copy link
Copy Markdown
Contributor

sonar-review-alpha Bot commented May 20, 2026

Summary

This PR replaces the peach-check analysis-consistency helper to detect issue count drops scoped to SonarJS-owned languages only.

What changed:

  • Switched from querying whole-project violations history (api/measures/search_history) to reconstructing unresolved-issue counts directly from api/issues/search
  • Issue counts are now filtered to 5 SonarJS languages: js, ts, css, web, and yaml
  • Issue reconstruction works by: fetching open issues created before each analysis point + resolving issues created before but closed after each analysis point

Why:
The old violations metric includes issues from all analyzers (non-SonarJS). When external analyzers fix issues or change their analysis scope, the signal was misinterpreted as a suspicious SonarJS drop. Filtering to SonarJS languages eliminates this noise and improves signal quality for release triage.

Impact:
Peach main-analysis triage will no longer flag false drops caused by non-SonarJS analyzer activity. The check becomes more reliable for identifying real SonarJS issues.

What reviewers should know

Start here: Begin with the main entry point fetchIssueCountsWithRetry and defaultFetchIssueCounts (lines 377–477) to understand the new reconstruction logic.

Key decisions to understand:

  1. Issue reconstruction timing (lines 436–476): For each analysis date, count unresolved issues by matching creation/close timestamps. Open issues are those created before the analysis point. Resolved issues are counted if they were created before AND closed after the analysis point.

  2. Language filtering (lines 36–37, 652–656): Supported languages are frozen at the top. The filter is applied directly in fetchIssuesPage by passing languages query parameter—this happens at query time, not post-processing.

  3. Pagination strategy (lines 507–561): When issue search exceeds 10k results, the code splits by creation date range to paginate efficiently, then falls back to component tree traversal if needed. This handles large projects without missing results.

  4. Temporal precision (lines 885–923): Timestamp formatting varies between ISO (.000Z) and Peach format (+0000). Watch for off-by-one errors in timestamp comparisons and the createdBefore exclusive boundary logic (line 444).

  5. Old API deprecated (INSTRUCTIONS.md, lines 175–177): The instructions now explicitly warn against using api/measures/search_history for this check—it cannot be filtered by language and hides false drops.

Test coverage clues: Tests verify language filtering is always present in issue queries, reconstruction accuracy across open/resolved/timing boundaries, and concurrency handling for large result sets. Run tests with node --test .claude/skills/peach-check/peach-issue-history.test.js.


  • Generate Walkthrough
  • Generate Diagram

🗣️ Give feedback

sonar-review-alpha[bot]

This comment was marked as resolved.

@francois-mora-sonarsource
Copy link
Copy Markdown
Contributor Author

Addressed the two open review notes in 4b33afe: exported SUPPORTED_LANGUAGES from the helper so the test imports the implementation constant directly, and renamed the report metric label from violations to sonarjs_issue_count to match the reconstructed issue-count semantics. Re-ran node --test .claude/skills/peach-check/peach-issue-history.test.js after the change.

sonar-review-alpha[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

@sonar-review-alpha sonar-review-alpha Bot left a comment

Choose a reason for hiding this comment

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

LGTM! ✅

🗣️ Give feedback

Comment thread .claude/skills/peach-check/peach-issue-history.js Outdated
Comment thread .claude/skills/peach-check/peach-issue-history.js
}

const onlyTestLikePaths = Object.keys(candidate.nonTestLikeCountsByBaselineState).length === 0;
const newRuleIds = Object.keys(candidate.topRulesByBaselineState.new ?? {});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Bug: Object.keys on array yields indices, not rule IDs

In diagnoseDrop, line 156 calls Object.keys(candidate.topRulesByBaselineState.new ?? {}). However, topRulesByBaselineState.new is an array of { rule_id, count } objects (produced by sortCountEntries), not a plain object. Object.keys() on an array returns string indices (['0', '1', ...]), not rule_id values.

The variable newRuleIds is only used for its .length check, so the behavior is accidentally correct (non-empty array → non-zero length). However, the variable name and usage pattern are misleading and would break if anyone later used the values.

Use the array directly instead of Object.keys; rename to reflect the actual type.:

const newRules = candidate.topRulesByBaselineState.new ?? [];
const onlyTestRuleAdditions =
    newRules.length === 0 ||
    newRules.every(entry => TEST_ONLY_RULES.has(entry.rule_id));
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 26, 2026

Code Review 👍 Approved with suggestions 2 resolved / 3 findings

Refactors Peach issue history to fetch scoped, language-specific counts while improving resilience with granular retries. Please correct the Object.keys usage in diagnoseDrop, as it currently iterates over array indices rather than rule IDs.

💡 Bug: Object.keys on array yields indices, not rule IDs

📄 .claude/skills/peach-check/peach-drop-forensics.js:156

In diagnoseDrop, line 156 calls Object.keys(candidate.topRulesByBaselineState.new ?? {}). However, topRulesByBaselineState.new is an array of { rule_id, count } objects (produced by sortCountEntries), not a plain object. Object.keys() on an array returns string indices (['0', '1', ...]), not rule_id values.

The variable newRuleIds is only used for its .length check, so the behavior is accidentally correct (non-empty array → non-zero length). However, the variable name and usage pattern are misleading and would break if anyone later used the values.

Use the array directly instead of Object.keys; rename to reflect the actual type.
const newRules = candidate.topRulesByBaselineState.new ?? [];
const onlyTestRuleAdditions =
    newRules.length === 0 ||
    newRules.every(entry => TEST_ONLY_RULES.has(entry.rule_id));
✅ 2 resolved
Performance: Resolved issues fetched without createdAfter, scanning all history

📄 .claude/skills/peach-check/peach-issue-history.js:480-491
fetchResolvedIssues (line 480) does not pass a createdAfter parameter to the API, even though oldestAnalysisTimestamp is available. This means the API returns every resolved issue ever created for the project (up to createdBefore), and the filtering to only issues closed after the oldest analysis happens client-side in normalizeResolvedIssues (line 668). For long-lived projects with many historical resolved issues this causes unnecessary API pagination and data transfer — and risks hitting the 10k result-window cap on resolved issues that were closed long before the analysis window even starts.

Since oldestAnalysisTimestamp is already passed to fetchResolvedIssues, it should also be forwarded as a createdAfter API parameter to let the server narrow the result set.

Performance: O(n*m) resolved-issue scan per analysis date

📄 .claude/skills/peach-check/peach-issue-history.js:445-459
In defaultFetchIssueCounts (lines 453-458), every analysis date triggers a full linear scan of all resolved issues to count those alive at that timestamp. With n analysis dates and m resolved issues, this is O(n × m). For projects with thousands of resolved issues and many analyses, this could become slow.

Since both targetAnalyses and resolved issues have timestamps, you could sort the resolved issues once and use a sweep-line approach (similar to the open-issue sliding index) to reduce this to O((n + m) log m).

🤖 Prompt for agents
Code Review: Refactors Peach issue history to fetch scoped, language-specific counts while improving resilience with granular retries. Please correct the Object.keys usage in diagnoseDrop, as it currently iterates over array indices rather than rule IDs.

1. 💡 Bug: Object.keys on array yields indices, not rule IDs
   Files: .claude/skills/peach-check/peach-drop-forensics.js:156

   In `diagnoseDrop`, line 156 calls `Object.keys(candidate.topRulesByBaselineState.new ?? {})`. However, `topRulesByBaselineState.new` is an **array** of `{ rule_id, count }` objects (produced by `sortCountEntries`), not a plain object. `Object.keys()` on an array returns string indices (`['0', '1', ...]`), not `rule_id` values.
   
   The variable `newRuleIds` is only used for its `.length` check, so the behavior is accidentally correct (non-empty array → non-zero length). However, the variable name and usage pattern are misleading and would break if anyone later used the values.

   Fix (Use the array directly instead of Object.keys; rename to reflect the actual type.):
   const newRules = candidate.topRulesByBaselineState.new ?? [];
   const onlyTestRuleAdditions =
       newRules.length === 0 ||
       newRules.every(entry => TEST_ONLY_RULES.has(entry.rule_id));

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@francois-mora-sonarsource francois-mora-sonarsource marked this pull request as draft May 28, 2026 08:19
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 29, 2026

Code Review 👍 Approved with suggestions 2 resolved / 3 findings

Refactors Peach issue history to fetch scoped, language-specific counts while improving resilience with granular retries. Please correct the Object.keys usage in diagnoseDrop, as it currently iterates over array indices rather than rule IDs.

💡 Bug: Object.keys on array yields indices, not rule IDs

📄 .claude/skills/peach-check/peach-drop-forensics.js:156

In diagnoseDrop, line 156 calls Object.keys(candidate.topRulesByBaselineState.new ?? {}). However, topRulesByBaselineState.new is an array of { rule_id, count } objects (produced by sortCountEntries), not a plain object. Object.keys() on an array returns string indices (['0', '1', ...]), not rule_id values.

The variable newRuleIds is only used for its .length check, so the behavior is accidentally correct (non-empty array → non-zero length). However, the variable name and usage pattern are misleading and would break if anyone later used the values.

Use the array directly instead of Object.keys; rename to reflect the actual type.
const newRules = candidate.topRulesByBaselineState.new ?? [];
const onlyTestRuleAdditions =
    newRules.length === 0 ||
    newRules.every(entry => TEST_ONLY_RULES.has(entry.rule_id));
✅ 2 resolved
Performance: Resolved issues fetched without createdAfter, scanning all history

📄 .claude/skills/peach-check/peach-issue-history.js:480-491
fetchResolvedIssues (line 480) does not pass a createdAfter parameter to the API, even though oldestAnalysisTimestamp is available. This means the API returns every resolved issue ever created for the project (up to createdBefore), and the filtering to only issues closed after the oldest analysis happens client-side in normalizeResolvedIssues (line 668). For long-lived projects with many historical resolved issues this causes unnecessary API pagination and data transfer — and risks hitting the 10k result-window cap on resolved issues that were closed long before the analysis window even starts.

Since oldestAnalysisTimestamp is already passed to fetchResolvedIssues, it should also be forwarded as a createdAfter API parameter to let the server narrow the result set.

Performance: O(n*m) resolved-issue scan per analysis date

📄 .claude/skills/peach-check/peach-issue-history.js:445-459
In defaultFetchIssueCounts (lines 453-458), every analysis date triggers a full linear scan of all resolved issues to count those alive at that timestamp. With n analysis dates and m resolved issues, this is O(n × m). For projects with thousands of resolved issues and many analyses, this could become slow.

Since both targetAnalyses and resolved issues have timestamps, you could sort the resolved issues once and use a sweep-line approach (similar to the open-issue sliding index) to reduce this to O((n + m) log m).

🤖 Prompt for agents
Code Review: Refactors Peach issue history to fetch scoped, language-specific counts while improving resilience with granular retries. Please correct the Object.keys usage in diagnoseDrop, as it currently iterates over array indices rather than rule IDs.

1. 💡 Bug: Object.keys on array yields indices, not rule IDs
   Files: .claude/skills/peach-check/peach-drop-forensics.js:156

   In `diagnoseDrop`, line 156 calls `Object.keys(candidate.topRulesByBaselineState.new ?? {})`. However, `topRulesByBaselineState.new` is an **array** of `{ rule_id, count }` objects (produced by `sortCountEntries`), not a plain object. `Object.keys()` on an array returns string indices (`['0', '1', ...]`), not `rule_id` values.
   
   The variable `newRuleIds` is only used for its `.length` check, so the behavior is accidentally correct (non-empty array → non-zero length). However, the variable name and usage pattern are misleading and would break if anyone later used the values.

   Fix (Use the array directly instead of Object.keys; rename to reflect the actual type.):
   const newRules = candidate.topRulesByBaselineState.new ?? [];
   const onlyTestRuleAdditions =
       newRules.length === 0 ||
       newRules.every(entry => TEST_ONLY_RULES.has(entry.rule_id));

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next
Copy link
Copy Markdown

Quality Gate passed Quality Gate passed

Issues
0 New issues
0 Fixed issues
0 Accepted issues

Measures
0 Security Hotspots
0 Dependency risks
No data about Coverage
No data about Duplication

See analysis details on SonarQube

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.

1 participant