Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: b8535f33-08ac-44ef-84d1-aab96770ae3a
5 KiB
Release Notes Formatting Guide
When creating or editing release notes content, follow these rules to ensure the markdown is well-formed and renders correctly on GitHub.
GitHub Auto-Linking
GitHub automatically links @username, #123, and @org/repo#123 references in release notes. Never use full URLs for these — use the shorthand forms:
@stephentoubnot[@stephentoub](https://github.com/stephentoub)#1234not[#1234](https://github.com/modelcontextprotocol/csharp-sdk/pull/1234)
This keeps the markdown source readable and avoids brittle links.
Exception — Full Changelog link: The **Full Changelog** compare link at the bottom of the release notes does not auto-link. It must use the full URL format:
**Full Changelog**: https://github.com/modelcontextprotocol/csharp-sdk/compare/previous-tag...new-tag
A bare previous-tag...new-tag will render as plain text, not a clickable link.
Writing the Release Body
Always compose the full release body as a complete markdown file before uploading. Never perform incremental string replacements on the body through shell commands or API calls — this risks collapsing newlines, introducing encoding artifacts, or corrupting the markdown structure.
Workflow for Updates
When the user requests changes to existing release notes:
- Fetch the current release body and save it to a local file
- Breaking change audit: Run the full breaking-changes skill audit on the commit range, just as for new release notes — this includes examining PRs, reconciling labels, offering to comment on PRs, and getting user confirmation. Also extract any breaking changes already documented in the existing release body; these must be preserved and reconciled with the audit results.
- Preamble check: Verify the release has a preamble (text before the first
##heading). If missing, compose one. The versioning documentation link belongs under the## Breaking Changesheading, not in the preamble, and must use thev{MAJOR}slug for the released version — see release-branches.md. - Write the entire corrected body to a separate local file (ensuring proper line breaks between all sections, entries, and paragraphs)
- Run
git diff --no-indexbetween the original and updated files and always present the raw diff output directly in the response as a fenced code block withdiffsyntax highlighting. Do not summarize or paraphrase the diff — always show the complete diff to the user. Require explicit confirmation before uploading. For published releases (not drafts), also offer to save the original body to a permanent local file, noting that GitHub does not retain prior versions of release notes. - Upload the complete file using
gh release edit --notes-file <file> - Verify the result by fetching the body again and checking that line count and structure are intact
Common Pitfalls
| Problem | Cause | Prevention |
|---|---|---|
| Body collapses to a single line | PowerShell string manipulation strips newlines during round-trip (variable assignment → .Replace() → WriteAllText) |
Always recreate the full body as a file rather than manipulating in-memory strings |
| UTF-8 BOM / garbage characters | [System.IO.File]::WriteAllText() with default encoding adds BOM |
Use UTF8Encoding($false) or write via a tool that produces BOM-less UTF-8 |
| Stray unicode characters | Encoding mismatch between shell output and file write | Avoid piping gh output through PowerShell variable assignment for content that will be written back |
Verification Checklist
After every release body update:
- Preamble exists before the first
##heading - If
## Breaking Changessection exists, it begins with the versioning docs link paragraph before the numbered list - The versioning docs link uses the
v{MAJOR}slug for the released version (e.g./v2/versioning.html), never the unslugged/versioning.html - Line count matches expected structure (~80+ lines for a typical release)
- Section headings (
## Breaking Changes,## What's Changed, etc.) each appear on their own line - Bullet entries are each on their own line
- No stray characters at the start of the body
- Preview the release on GitHub to confirm rendering
AI Disclosure
A draft release body created by an agent carries a concise AI-generated disclosure at the bottom:
> [!NOTE]
> These release notes were drafted with GitHub Copilot and reviewed before publishing.
Keep it on the draft. Removing it is the user's decision, made as part of their final sign-off once they have reviewed the notes line by line and are satisfied the content is theirs. Never remove it on your own initiative, and never reintroduce it after the user has removed it.
This exception applies only to release notes, which get a dedicated human review before publishing. Disclosures on pull request descriptions, issues, and comments always remain.