Repo Gardening: Add AI-based check for user-facing PR changes#46701
Repo Gardening: Add AI-based check for user-facing PR changes#46701
Conversation
Fixes MONOREP-325 Add a new checkIfDocsNeeded task that uses OpenAI to analyze PR diffs and descriptions to determine if changes are user-facing. If detected with medium/high confidence, the [Status] UI Changes label is applied. The task: - Fetches and cleans PR diffs (filtering lock files, binary files, minified code) - Sends to OpenAI for analysis - Applies [Status] UI Changes label for user-facing changes - Uses [Experiment] AI UI check marker label to prevent re-processing - Bails out for drafts, bots, reverts, and already-labeled PRs
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
22f63b4 to
f62b42c
Compare
… task Add documentation and Slack notification support for the AI-based user-facing changes detection task: - Create task readme.md with usage and bailout conditions - Update main README.md to include task in the list - Send Slack notification to quality channel when PR is flagged
f62b42c to
4bab131
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds an AI-powered task to automatically detect user-facing changes in closed PRs. The task uses OpenAI's API to analyze PR diffs and descriptions, applying a [Status] UI Changes label when user-facing changes are detected with medium or high confidence. A Slack notification is sent to the quality channel when configured.
Changes:
- New
checkIfDocsNeededtask that runs onpull_request_targetclosed events - New
getDiffutility to fetch and cache PR diffs from GitHub API - Integration with existing OpenAI and Slack utilities for analysis and notifications
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
src/tasks/check-if-docs-needed/index.js |
Main task implementation with AI analysis logic, label application, and Slack notifications |
src/tasks/check-if-docs-needed/readme.md |
Documentation explaining the task's purpose, behavior, and bailout conditions |
src/utils/get-diff.js |
Utility function to fetch PR diffs with caching, filtering, and size limits |
src/index.js |
Registers the new task to run on PR closed events with fork protection |
changelog/2026-01-20-18-55-37-299081 |
Changelog entry documenting the new feature |
README.md |
Updated task list to include the new checkIfDocsNeeded task |
…cter sanitization Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…-needed/index.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…-needed/index.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…-needed/index.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…-needed/index.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…-needed/index.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@jeherve could you replace |
Replace slack_quality_channel with slack_product_ambassadors_channel for the check-if-docs-needed task to send notifications to the #jetpack-product-ambassadors channel instead.
| content = content.replace( /\\/g, '\\\\' ); | ||
|
|
||
| return content.replace( /`/g, '\\`' ); |
There was a problem hiding this comment.
The AI that wrote this doesn't know about using /[\\`]/g to escape both characters at once?
Also potentially of note is that a \ doesn't actually escape backticks inside backticks in (GitHub-flavored) markdown in the first place. You have to increase the number of framing backticks there too, as I did above.
| // Replace sequences of three or more backticks (which could break markdown code blocks) | ||
| // with a safe placeholder. This prevents prompt injection via code block delimiters. | ||
| // This is the critical fix: triple backticks would prematurely close the code block. | ||
| content = content.replace( /```+/g, '[code-fence]' ); |
There was a problem hiding this comment.
In actual GitHub-flavored Markdown, you could just ensure that the real fence has more backticks than the content. For example,
This makes a code block:
```
code block!
```
I have no idea what the AI might need though.
|
|
||
| return `You are analyzing a GitHub Pull Request to determine if users will experience something DIFFERENT after this change is deployed. | ||
|
|
||
| The key question is: "Will users notice any difference in behavior, appearance, or functionality?" |
There was a problem hiding this comment.
Is this the right question? We don't care about all user-facing changes, just ones that will need updates to the support documentation.
| ${ sanitizedTitle } | ||
|
|
||
| Here is the PR description: | ||
| ${ sanitizedBody || '(No description provided)' } |
There was a problem hiding this comment.
You're not fencing this at all, and it might be long. How does the AI know that everything up to "Here is the code diff" isn't instruction? Particularly since it probably contains headings and all sorts of stuff.
| return; | ||
| } | ||
|
|
||
| // Skip if PR title contains "revert" (case-insensitive). |
There was a problem hiding this comment.
"Undo revert and fix feature with a bunch of additional UI changes"?
"New UI for post revert feature"?
There was a problem hiding this comment.
Switched to a stricter approach (only look for revert at the beginning of the title) in ca0c4c1
| // Check if OpenAI API key is provided. | ||
| const apiKey = getInput( 'openai_api_key' ); | ||
| if ( ! apiKey ) { | ||
| debug( `check-if-docs-needed: No OpenAI key is provided. Bail.` ); |
There was a problem hiding this comment.
This seems like it would be useful to output to the log even when not in testing mode. "Why did this fail? There's no diagnostic output."
Same goes for many of the error messages below.
| result = JSON.parse( response ); | ||
| } catch ( error ) { | ||
| debug( | ||
| `check-if-docs-needed: Failed to parse OpenAI response for PR #${ number }: ${ error }` |
There was a problem hiding this comment.
It may be useful to know what OpenAI did respond with, not just the parse error.
| return; | ||
| } | ||
|
|
||
| const isUserFacing = typeof result?.is_user_facing === 'boolean' ? result.is_user_facing : false; |
There was a problem hiding this comment.
If it's not boolean, that probably deserves diagnostic output too.
| const confidence = | ||
| result?.confidence && | ||
| typeof result.confidence === 'string' && | ||
| [ 'low', 'medium', 'high' ].includes( result.confidence.trim().toLowerCase() ) | ||
| ? result.confidence.trim().toLowerCase() | ||
| : 'low'; |
There was a problem hiding this comment.
Same for this. Instead of (just) assuming 'low', we should probably also log the unexpected data.
| // Remove unwanted file blocks (e.g., lockfiles) before further processing/truncation. | ||
| diff = filterDiff( diff ); | ||
|
|
||
| // Filter out lines longer than 500 characters (likely minified code). |
There was a problem hiding this comment.
This may result in broken diffs.
At a quick grep, this will also catch a fair bit of inline SVG code. And a few paragraphs in various markdown docs.
There's also the question as to why we're committing minified code in the first place.
There was a problem hiding this comment.
58fb4b3 should solve that, although I don't know if the added complexity is worth it in this case ; I don't know that it's important for the LLM to get a valid diff since it doesn't have to apply it anywhere. I suppose it could become important in the future.
There's also the question as to why we're committing minified code in the first place.
Probably a larger discussion to have :)
There was a problem hiding this comment.
I guess my thinking was that it might confuse the AI to have broken/empty diff chunks like
diff --git a/some/file.min.js b/some/file.min.js
index aaaaaaaaaaaa..bbbbbbbbbb 100644
--- a/some/file.min.js
+++ b/some/file.min.js
@@ -1 +1
\ No newline at end of file
diff --git a/some/other.file b/some/other.file
...
In GitHub-flavored Markdown, backslashes don't escape backticks inside code blocks. The proper approach is to use more backticks for the fence than appear in the content. - Use 4 backticks for code fences in the prompt - Only replace sequences of 4+ backticks in content (instead of 3+) - Remove unnecessary backslash and backtick escaping
The prompt previously asked about user-facing changes in general. Updated to specifically ask whether support documentation would need to be updated, which is the actual goal of this feature.
Wrap the PR title and description in code fences so the AI can clearly distinguish between instruction text and PR content. This prevents the AI from potentially interpreting PR content (which may contain markdown formatting) as additional instructions.
Only skip PRs where the title starts with "Revert" (the standard GitHub revert format), instead of skipping any PR with "revert" anywhere in the title. This prevents incorrectly skipping PRs like "Undo revert and fix feature" or "New UI for post revert feature".
- Include PR number in the missing API key message - Include the actual OpenAI response when JSON parsing fails - Log when is_user_facing is not a boolean with the actual value - Log when confidence is not a valid value with the actual value This makes it easier to diagnose issues when the AI returns unexpected responses.
The previous implementation filtered all lines longer than 500 characters, which could break diff structure by removing header lines. Now: - Preserve diff header lines (diff --, +++, ---, @@, etc.) regardless of length - Only filter actual content lines (additions, deletions, context) - Add logging when lines are filtered - Add comments explaining the tradeoff with SVG/markdown content
Update references from "quality channel/team" to "product ambassadors channel/team" to match the actual parameter name `slack_product_ambassadors_channel`.
@StefMattana Sure thing, done! |
anomiex
left a comment
There was a problem hiding this comment.
Seems reasonable. Several comments inline, but nothing that's both too important and also is actionable IMO.
| * | ||
| * In GitHub-flavored Markdown, backslashes don't escape backticks inside code blocks. | ||
| * Instead, you use more backticks for the fence than appear in the content. | ||
| * We use 4 backticks for our fences, so we replace any sequence of 4+ backticks. |
There was a problem hiding this comment.
Optional: We could have this function add the fencing backticks itself after scanning the content to see how many are necessary, instead of arbitrarily limiting it.
There was a problem hiding this comment.
Oh, I see what you mean. I like that. I'll keep it in mind if this becomes a problem ; I'm thinking the LLM will tolerate markup issues a bit more than a human would so this may never come up.
| return `You are analyzing a GitHub Pull Request to determine if it contains changes that would require updates to user-facing support documentation. | ||
|
|
||
| The key question is: "Will users notice any difference in behavior, appearance, or functionality?" | ||
| The key question is: "Would support documentation need to be updated to reflect these changes?" |
There was a problem hiding this comment.
It seems like we're basically asking the AI to guess what might be on the documentation site and what might not. I wonder how well that'll work out?
This may be completely infeasible, but could we point it at the documentation we're concerned with?
Things that seem like they'd make it infeasible include if some of the documentation is private for HEs, if the AI can't access the web, or if the AI can access the web but would get bogged down trying to access the whole documentation site.
There was a problem hiding this comment.
Yeah, I think it's best to keep it general for now, and we can see how accurate it is. If it makes a lot of mistakes, we could consider giving it access to the support docs to start.
| debug( `check-if-docs-needed: PR #${ number } title contains "revert". Skipping.` ); | ||
| // Skip if PR title starts with "Revert" (the standard GitHub revert format). | ||
| // This avoids false positives like "Undo revert and fix..." or "New UI for post revert feature". | ||
| if ( /^revert\b/i.test( title ) ) { |
There was a problem hiding this comment.
Should we cover a few more cases like
| if ( /^revert\b/i.test( title ) ) { | |
| if ( /^revert(|ed|ing)\b/i.test( title ) ) { |
There was a problem hiding this comment.
Looking at previous revert PRs in this repo, I think just testing for "Revert" is enough, that seems to be used every time.
| // We keep diff header lines (diff --, +++, ---, @@, etc.) regardless of length to avoid | ||
| // breaking the diff format. Only actual content lines (starting with +, -, or space) are filtered. | ||
| // Note: This may also filter legitimate long lines like inline SVG or long markdown paragraphs, | ||
| // but such lines are typically not useful for AI analysis of user-facing changes. |
There was a problem hiding this comment.
SVGs might be relevant to evaluating whether a screenshot needs updating. Although above we only ask the AI about changed text that appears in screenshots.
| line.startsWith( 'diff --git' ) || | ||
| line.startsWith( '---' ) || | ||
| line.startsWith( '+++' ) || | ||
| line.startsWith( '@@' ) || | ||
| line.startsWith( 'index ' ) || | ||
| line.startsWith( 'new file' ) || | ||
| line.startsWith( 'deleted file' ) || | ||
| line.startsWith( 'Binary files' ) |
There was a problem hiding this comment.
I wonder whether this would be better as a heuristic for what to keep:
- Line does not start with
,+,-, or\ No newline. - Line starts with
---or+++(or even--- a/or+++ b/).
There was a problem hiding this comment.
I'm thinking the existing list is maybe clearer, this way we know exactly what we keep, no exceptions.
| slack_editorial_channel: ${{ vars.SLACK_EDITORIAL_CHANNEL }} | ||
| slack_he_triage_channel: ${{ vars.SLACK_HE_TRIAGE_CHANNEL }} | ||
| slack_quality_channel: ${{ vars.SLACK_QUALITY_CHANNEL }} | ||
| slack_product_ambassadors_channel: ${{ vars.SLACK_PRODUCT_AMBASSADORS_CHANNEL }} |
There was a problem hiding this comment.
Thanks for documenting the new variable at PCYsg-xsv-p2 ! 🙂
Fixes MONOREP-325
Proposed changes:
This PR adds a new
checkIfDocsNeededtask to the repo-gardening action that uses AI to automatically detect user-facing changes in PRs. When detected with medium or high confidence, it:[Status] UI Changeslabel to flag the PR for documentation review#jetpack-developer-ambassadorsin the Jetpakc repo).Since it's a new task, it will only be enabled in the Jetpack repo for now. In the future, it can be enabled in other repos too.
Bailout conditions:
The task skips processing when:
openai_api_keyinput is provided[Status] UI ChangeslabelOther information:
Jetpack product discussion
N/A
Does this pull request change what data or activity we track or use?
No. The task sends PR diffs and descriptions to OpenAI for analysis but does not store or track any new data.
Testing instructions:
This is easier tested in a fork. Here are 2 examples.