Skip to content

Commit d024259

Browse files
authored
Tighten release notes prompt and clean input data (#6068)
1 parent e003c86 commit d024259

2 files changed

Lines changed: 74 additions & 100 deletions

File tree

src/llms/prompts.py

Lines changed: 51 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -123,107 +123,61 @@
123123
124124
Generate the release notes in proper OpenSearch format:"""
125125

126-
AI_RELEASE_NOTES_PROMPT_COMMIT_OPENSEARCH = """I need you to generate OpenSearch component release notes from commit data. Please follow the OpenSearch release notes format exactly.
126+
AI_RELEASE_NOTES_PROMPT_COMMIT_OPENSEARCH = """Generate release notes for {component_name} {version} from the commit data below.
127127
128-
**Component Information:**
129-
- Component Name: {component_name}
130-
- Version: {version}
131-
- Repository URL: {repository_url}
128+
Repository: {repository_url}
132129
133130
**Commit Data:**
134131
{commits_text}
135132
136-
**Instructions:**
137-
138-
1. **Content filtering — apply BEFORE categorization:**
139-
- Do not add the pull request that has title starting with "[AUTO] Increment version to".
140-
- Do not add any PR with the `skip-changelog` label, regardless of content.
141-
- Exclude any commit/revert pairs as the net result is no change.
142-
- **Exclude the following non-user-facing changes:**
143-
* Test additions, modifications, fixes, or refactoring (including flaky test fixes, new integration tests,
144-
test infrastructure improvements, and test cleanup). A PR whose description says "Add test for X" or
145-
"Cleanup X in tests" is non-user-facing even if the underlying feature is user-facing.
146-
* Build and CI changes: GitHub Actions version bumps (e.g. actions/setup-java, actions/upload-artifact,
147-
peter-evans/create-pull-request, lycheeverse/lychee-action, tj-actions/*), Gradle build changes,
148-
Docker base image updates, CI pipeline configuration.
149-
* Dependency bumps that only affect test fixtures or build tooling (e.g. bumps under /test/fixtures/,
150-
/buildSrc/, or test-only libraries like wiremock).
151-
* Release machinery: changelog fixes, release notes commits, README edits.
152-
* Internal code refactoring that does not change any user-facing behavior, API, or configuration.
153-
This includes deprecation warning fixes, code cleanup, and removing unused internal code/plugins.
154-
* Maintainer list changes.
155-
* Incremental PRs for a larger feature (these should already have one entry from the main PR).
156-
- A PR matching an exclusion rule above must be excluded even if its labels would match a category.
157-
- Use the commit message, PR description, and labels to make this judgment.
158-
- When uncertain whether a change is user-facing, **include it** — a human reviewer can remove it later.
159-
160-
2. **Categorization — only for PRs that survive filtering:**
161-
- First, check if any labels match these categories (case-insensitive, partial matches allowed):
162-
* "breaking change" or "breaking" → Breaking Changes
163-
* "feature" or "feat" → Features
164-
* "enhancement" or "improve" → Enhancements
165-
* "bug" or "fix" or "bugfix" → Bug Fixes
166-
* "maintenance" or "version" or "support" → Maintenance
167-
- If no labels match, analyze the Message content, PullRequestSubject, and PR Description to determine
168-
the appropriate category:
169-
* Features: A net new unit of functionality that satisfies a requirement, represents a design decision,
170-
and provides a potential configuration option. For improvements on existing features, use Enhancements.
171-
For fixes on existing features, use Bug Fixes.
172-
* Enhancements: Improves the performance, usability, or reliability of an existing feature without
173-
changing its core functionality.
174-
* Bug Fixes: Resolves an issue or defect in the software.
175-
* Maintenance: Routine upkeep such as dependency updates that ship in the distribution.
176-
- Do not use "Infrastructure", "Documentation", or "Refactoring" as categories. Changes that would
177-
belong to those categories should have been excluded by the filtering step.
178-
179-
3. **Entry Format:**
180-
- Use the PullRequestSubject and PR Description as input, but rewrite each entry as a concise, clear one-line
181-
summary. The target audience is OpenSearch users — end-users, operators, and system administrators — not developers.
182-
Do not simply copy the PR subject verbatim; improve clarity and consistency.
183-
- Extract PR number from PullRequestSubject (format: (#123))
184-
- Format: `* <description> ([#<number>]({repository_url}/pull/<number>))`
185-
- Always use asterisk (*) for bullet points
186-
- Always wrap PR links in parentheses
187-
- Make sure first character of each entry is capitalized.
188-
- Group related commits into a single entry when appropriate (e.g., multiple PRs implementing parts of the same
189-
feature, or a series of dependency bumps for the same library).
190-
191-
4. **Output Requirements:**
192-
- The main heading with ## should be "version number Release Notes" (e.g., For version 3.2.0 ## Version 3.2.0
193-
Release Notes followed by a blank line and then "Compatible with OpenSearch and OpenSearch Dashboards
194-
version <version number>" followed by content)
195-
- Generate markdown with ### headers for each category
196-
- Only include categories that have entries
197-
- Sort categories in this order: Breaking Changes, Features, Enhancements, Bug Fixes, Maintenance
198-
- Each entry should be a single line with proper PR link formatting
199-
200-
5. **PR Link Format:**
201-
- Extract PR number from PullRequestSubject
202-
- Format as: `([#<number>]({repository_url}/pull/<number>))`
203-
- Example: `([#456](https://github.com/opensearch-project/OpenSearch/pull/456))`
204-
205-
6. **Important Notes:**
206-
- If you cannot determine the appropriate category from labels OR content analysis, place the entry in an "Unknown" category
207-
- Prioritize Message over PullRequestSubject for determining category when using fallback analysis
208-
209-
7. **Borderline Calls:**
210-
After the release notes, add a section starting with `<!-- BORDERLINE_CALLS` and ending with `-->`.
211-
Inside this HTML comment, list any judgment calls where the categorization, inclusion, or exclusion decision
212-
was debatable. For each, reference the PR number and briefly explain the decision and alternatives. Examples:
213-
- A PR labeled `bug` that reads more like a behavioral change
214-
- Grouping multiple PRs into a single entry
215-
- Including a PR that could reasonably be considered non-user-facing
216-
- Excluding a PR that could reasonably be considered user-facing
217-
- Choosing one category over another when both fit
218-
219-
Format:
220-
```
221-
<!-- BORDERLINE_CALLS
222-
- #1234: Excluded as non-user-facing (test fix) — but it changes test infrastructure that plugin authors rely on, so could be included under Infrastructure.
223-
- #5678: Placed in **Enhancements** — could also be **Bug Fixes** since it changes existing behavior to fix a usability issue.
224-
- #9012 + #9013: Grouped into one entry — both implement parts of the same feature.
225-
-->
226-
```
227-
If there are no borderline calls, omit this section entirely.
133+
**Filtering (apply first):**
134+
- Exclude PRs titled "[AUTO] Increment version to...".
135+
- Exclude PRs with the `skip-changelog` label.
136+
- Exclude commit/revert pairs.
137+
- Exclude non-user-facing changes: test-only changes, CI/build changes, GitHub Actions bumps,
138+
release machinery (changelogs, READMEs), internal refactoring with no behavior/API/config
139+
change, maintainer list changes, and incremental PRs for a feature already covered by another entry.
140+
- Exclusion rules override label-based categorization.
141+
- When uncertain, include — a human reviewer can remove it later.
142+
143+
**Categorization (for surviving PRs):**
144+
- Match labels first (case-insensitive, partial match):
145+
"breaking"→Breaking Changes, "feature"/"feat"→Features, "enhancement"/"improve"→Enhancements,
146+
"bug"/"fix"/"bugfix"→Bug Fixes, "maintenance"/"version"/"support"→Maintenance
147+
- If no label matches, categorize by content:
148+
Features=net new functionality, Enhancements=improves existing feature,
149+
Bug Fixes=fixes a defect, Maintenance=dependency updates and routine upkeep.
150+
- Only use these 5 categories. If none fit, use "Unknown".
151+
152+
**Entry format:**
153+
`* <concise one-line summary for end-users/operators> ([#<number>]({repository_url}/pull/<number>))`
154+
- Rewrite PR subjects for clarity; do not copy verbatim. Capitalize first character.
155+
- Group related PRs into a single entry when appropriate.
156+
157+
Example rewrites:
158+
PR Subject: "Add support for warm index pre-loading of global ordinals on replica shards with segment replication (#20650)"
159+
Good: * Add index warmer support for replica shards using segment replication ([#20650](https://github.com/opensearch-project/OpenSearch/pull/20650))
160+
Bad: * Add WarmerRefreshListener to NRTReplicationEngine to warm replica shards ([#20650](https://github.com/opensearch-project/OpenSearch/pull/20650))
161+
Why: The "good" version uses user-facing language. The "bad" version leaks internal class names.
162+
163+
**Output format:**
164+
165+
## Version {version} Release Notes
166+
167+
Compatible with OpenSearch and OpenSearch Dashboards version {version}
168+
169+
### Breaking Changes
170+
* ...
171+
172+
### Features
173+
* ...
174+
175+
- Only include categories that have entries.
176+
- Order: Breaking Changes, Features, Enhancements, Bug Fixes, Maintenance.
177+
178+
**Borderline calls:**
179+
After the release notes, add `<!-- BORDERLINE_CALLS ... -->` listing any debatable
180+
filtering, categorization, or grouping decisions with PR numbers and brief rationale.
181+
Omit if none.
228182
229183
Generate the release notes in proper OpenSearch format:"""

src/release_notes_workflow/release_notes.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ def __init__(self, input_manifests: List[InputManifest], date: str, action_type:
3131
self.token = os.getenv('GITHUB_TOKEN')
3232
self.filter_commits = ['flaky-test', 'testing', 'skip-changelog']
3333

34+
@staticmethod
35+
def _clean_message(text: str) -> str:
36+
"""Strip Signed-off-by / Co-authored-by tags from flattened commit messages."""
37+
text = re.sub(r'\s*(Signed-off-by|Co-authored-by):\s*\S+.*?(?=\s*(?:Signed-off-by|Co-authored-by):|\s*$)', '', text)
38+
return re.sub(r'\s{2,}', ' ', text).strip()
39+
40+
@staticmethod
41+
def _clean_pr_body(text: str) -> str:
42+
"""Strip HTML comments, Check List sections, and DCO boilerplate from PR body."""
43+
# Remove HTML/markdown comments <!-- ... -->
44+
text = re.sub(r'<!--.*?-->', '', text, flags=re.DOTALL)
45+
# Remove Check List section (### Check List ... until next ### or end)
46+
text = re.sub(r'###?\s*Check\s*List.*?(?=###|\Z)', '', text, flags=re.DOTALL | re.IGNORECASE)
47+
# Remove Related Issues section (### Related Issues ... until next ### or end)
48+
text = re.sub(r'###?\s*Related\s*Issues.*?(?=###|\Z)', '', text, flags=re.DOTALL | re.IGNORECASE)
49+
# Remove DCO boilerplate paragraph
50+
text = re.sub(r'By submitting this pull request.*?Apache 2\.0 license\.?.*?(?=\n\n|\n#|\Z)', '', text, flags=re.DOTALL)
51+
return text.strip()
52+
3453
@staticmethod
3554
def _extract_borderline_calls(raw: str) -> Tuple[str, str]:
3655
"""Extract borderline calls HTML comment from LLM output, returning (release_notes, borderline_calls)."""
@@ -143,15 +162,16 @@ def generate(self, args: ReleaseNotesCheckArgs, component: InputComponentFromSou
143162
final_commits = [doc for doc in commits if not set(doc['Labels']) & set(self.filter_commits)]
144163
commits_text = ""
145164
for i, commit in enumerate(final_commits, 1):
146-
message = commit.get("Message", "")
165+
message = self._clean_message(commit.get("Message", ""))
147166
labels = commit.get("Labels", [])
148167
pr_subject = commit.get("PullRequestSubject", "")
149-
pr_body = commit.get("PullRequestBody", "")
168+
pr_body = self._clean_pr_body(commit.get("PullRequestBody", ""))
150169

151170
commits_text += f"{i}. PR Subject: {pr_subject}\n"
152171
commits_text += f" Message: {message}\n"
153172
commits_text += f" Labels: {', '.join(labels) if labels else 'None'}\n"
154-
if pr_body:
173+
# dependabot PRs have a lot of unneeded details in PR bodies
174+
if pr_body and 'dependabot' not in labels:
155175
# Truncate very long PR bodies to avoid exceeding token limits
156176
truncated_body = pr_body[:2000] + "..." if len(pr_body) > 2000 else pr_body
157177
commits_text += f" PR Description: {truncated_body}\n"

0 commit comments

Comments
 (0)