---
name: changelog
description: Polish the automated CHANGELOG on develop before the next stable build. Removes GUS refs, categorizes under-the-cover changes, improves customer-facing descriptions. Use when preparing/reviewing the changelog after a weekly prerelease promotion, or when user mentions changelog quality.
review: never
---

# Changelog Polish

Improve the automated changelog delta committed to `develop` by `promote-to-prerelease.yml`.

Scope: the all-extensions release changelog at `packages/salesforcedx-vscode/CHANGELOG.md`. Root `CHANGELOG.md` contains full historical changelog (automatically prepended by `scripts/prepend-release-changelog.js`, run as part of the same promote job) — do not edit manually. Per-package `CHANGELOG.md` files (e.g. `packages/salesforcedx-vscode-i18n/CHANGELOG.md`) are scoped to their own package and out of scope here.

## When to use

- User invokes `/changelog` or asks to prepare/review the changelog
- On `develop`, after a `chore: changelog for prerelease vX.Y.Z` commit lands
- **Timing matters**: polish it *before* `build-github-release.yml`'s next scheduled run (Wednesdays, 7 AM UTC). That job reads develop's current `packages/salesforcedx-vscode/CHANGELOG.md`, relabels the header to the stable version, and bakes it into the stable VSIX. Once that's run, the content is frozen inside the built artifact — fixing it afterward means manually re-injecting into the release's VSIX asset, not editing this file.

## File location

`packages/salesforcedx-vscode/CHANGELOG.md` on `develop`

## Workflow

1. **Checkout/pull `develop`** and **read** the current changelog file
2. **Identify** the automated commit: `git log --oneline -- packages/salesforcedx-vscode/CHANGELOG.md | grep "changelog for prerelease"`
3. **For each entry**, fetch PR context: `gh pr view <number> --json title,body,commits,labels`. In the body, look for a **"What issues does this PR fix or reference?"** section and extract any issue or discussion numbers linked there.
4. **Apply** the rules below to produce a polished draft
5. **Present** the revised changelog to the user for approval before writing

## Rules

### 1. Remove GUS work item references

Strip any `W-NNNNNNN` or `- W-NNNNNNN` text from entries. These are internal tracking IDs not meaningful to customers.

Before: `- Add a Max Rows input to SOQL Builder UI - W-22199672 ([PR #7261](...`
After: `- We added a **Max Rows** input to SOQL Builder UI so you can limit the number of rows retrieved. ([PR #7261](...`

### 2. Classify entries as customer-facing or under-the-cover

**Under-the-cover** (not visible to users):
- Test infrastructure (E2E suites, unit tests, test utilities)
- Internal telemetry/observability (span attributes, logging)
- Refactoring with no behavior change
- CI/CD pipeline changes
- Dependency bumps with no user-visible effect
- Internal API changes between packages

**Customer-facing** (visible to users):
- New commands, UI elements, or features
- Bug fixes that affected user workflows
- Performance improvements users would notice
- Behavior changes in existing features

Use PR commits, title, body, and labels to decide. When uncertain, check the diff: `gh pr diff <number> --name-only` to see which files changed.

Under-the-cover entries go under a dedicated `## Under the Hood` section (no package sub-headers needed). Consolidate all under-the-cover PRs into one or a few lines:
`- We made some under the hood changes. ([PR #NNNN](...), [PR #MMMM](...))`.

### 3. Deduplicate multi-package entries

The automation lists the same PR under every package it touched. Consolidate to the **most relevant user-facing package**. If a change touched `salesforcedx-vscode-services` plus a feature extension, list it under the feature extension only.

### 4. Improve customer-facing descriptions

- Write from the user's perspective: "We added...", "We fixed...", "You can now..."
- Describe what the user can do or what changed for them, not the implementation
- Start with `We added…`, `We fixed a bug where…`, `We improved…`, `We reverted…`, or `The <X> now…`. Full sentences ending in `.`
- **Bold** user-facing names: extensions (**Salesforce Metadata Visualizer**), commands (**SFDX: Create Project**), UI elements (**Run** button, **Org Differences** view, **Ready for Review**)
- Backticks for code identifiers, file names, config keys: `jsconfig.json`, `sourceApiVersion`, `MetadataRegistryService`, `.soql`
- Keep entries to 1-2 sentences max
- For opaque/internal fixes where customer impact is unclear: `We made some changes under the hood.`
- For reverts: `We reverted <X> because <reason>.`
- For setting/option additions: name the setting in **bold**, describe default behavior
- For extension-pack additions: include extension id in parens, e.g. `**Salesforce Live Preview** (salesforce.salesforcedx-vscode-ui-preview)`
- Prefer `VS Code` over `VSCode`; `on Windows` not `in Windows`
- After the PR link(s), append issue and discussion links found in the **"What issues does this PR fix or reference?"** section of the PR body:
  - Issues: `[ISSUE #NNNN](https://github.com/forcedotcom/salesforcedx-vscode/issues/NNNN)`
  - Discussions: `[DISCUSSION #NNNN](https://github.com/forcedotcom/salesforcedx-vscode/discussions/NNNN)`
  - Format: `([PR #7517](...), [ISSUE #4065](...))`
  - Only include issues/discussions explicitly listed in that section; do not infer from commit messages or other body text

### 5. Verify categories

- `## Added` — new features, new commands, new UI
- `## Fixed` — bug fixes
- `## Changed` — behavior changes to existing features (add section if needed)
- `## Under the Hood` — internal changes not visible to users (CI, telemetry, refactoring, dep bumps). No package sub-headers needed.
- Remove empty package sections (header with no entries)

## Example transformation

**Automated:**
```markdown
## Added

#### salesforcedx-vscode-metadata

- Filter packageDirs to those containing target folder - W-22049669 ([PR #7225](...))

#### salesforcedx-vscode-services

- Filter packageDirs to those containing target folder - W-22049669 ([PR #7225](...))

- Replace static activationEvents with programmatic activation W-21956120 ([PR #7154](...))
```

**Polished:**
```markdown
## Added

#### salesforcedx-vscode-metadata

- When creating an Apex class or LWC component, the output directory picker now lists only package directories that contain the relevant folder. ([PR #7225](...))

## Under the Hood

- We made some under the hood changes. ([PR #7154](...))
```

## Commit and push

After user approves the polished changelog:

1. Make sure you're on `develop` and up to date: `git checkout develop && git pull`
2. Stage **only** the changelog file: `git add packages/salesforcedx-vscode/CHANGELOG.md`
3. Verify no other files are staged: `git diff --cached --name-only` must show only `packages/salesforcedx-vscode/CHANGELOG.md`. If other files appear, unstage them before committing.
4. Commit: `git commit -m "chore: polish changelog [skip ci]"`
5. Push: `git push origin develop`

Never include other file changes in this commit.

Use these `chore:` subjects for polish commits on `develop`:

- `chore: polish changelog`
- `chore: write into sentences`
- `chore: changelog improvements`
- `chore: update changelog`
- `chore: missing changelog entry`
- `chore: clean up duplicate entries`
- `chore: merge vX.Y.A and vX.Y.B changelogs` (when prior patch's entries need to roll into current)

---

## Reference: changelog lifecycle

`promote-to-prerelease.yml` runs 3-stage pipeline to generate & polish changelog before commit to `develop`. Background context; typically not interacted with during manual polish.

### Compute → Polish (AI) → Commit → Review (human)

1. **compute-changelog-range** (promote-to-prerelease.yml): Outputs `fromRef` = newest `marketplace-prerelease-*` tag (set by last week's promote run) or latest stable `v*` tag (correct fallback before any tracking tag exists). Empty `fromRef` (identical to this week's nightly commit — a manual re-run or hotfix) skips changelog-body instead of feeding it an identical range.
2. **changelog-body** (reusable workflow `.github/workflows/changelog-body.yml`): Calls Cursor-backed AI (guided by `.cursor/skills/changelog-judgment/SKILL.md`) to produce polished, customer-facing markdown body from commits in range `(fromRef, toRef]`. Already removes GUS refs, rewrites sentences ("We added/fixed/improved..."), dedupes multi-package PRs, consolidates Under-the-Hood. Returns `body` output.
3. **write-changelog** (promote-to-prerelease.yml): Writes header `# <version> - <date>` + body to `packages/salesforcedx-vscode/CHANGELOG.md`. Immediately runs `scripts/prepend-release-changelog.js` to copy same content to root `CHANGELOG.md`. Both labeled with **prerelease** version (e.g. `67.17.9`). Committed directly to `develop`.
4. **Polish (optional)** (`develop`, this skill): Human review/touch-up before next `build-github-release.yml` run. Model-generated body is typically ready, but catch edge cases/errors using Rules 1–5 as checklist. **Note:** because prepend already ran in step 3, root `CHANGELOG.md`'s copy of this version's section is *not* automatically re-synced by a polish edit.
5. **Relabel to stable** (`build-github-release.yml`, next Wednesday 7 AM UTC): Pulls develop's current `packages/salesforcedx-vscode/CHANGELOG.md` (picking up any polish from step 4), relabels header from prerelease → stable version, bakes into stable VSIX.
6. **Relabel history** (`publishVSCode.yml`, on final stable publish): Relabels same header in both changelogs from prerelease → stable, so history matches what shipped.

`prepend-release-changelog.js` validates structure (version format, file existence, non-empty), runs idempotent (skips if already in root), reports errors (ENOSPC, EACCES, missing files).

### Commit details

- Commit: `chore: changelog for prerelease vXX.YY.ZZ [skip ci]`
- Range: previous week's promoted prerelease to nightly SHA (disjoint by construction — changelog-body doesn't read the existing file, so there's nothing to dedupe against)
- Skipped if changelog-body returns an empty body (no `feat`/`fix`/`perf` commits in range)

### Format

```
# XX.YY.ZZ - Month DD, YYYY

## Added

#### <package-name>

- <message> ([PR #<num>](https://github.com/forcedotcom/salesforcedx-vscode/pull/<num>))

## Fixed

#### <package-name>

- <message> ([PR #<num>](...))

## Under the Hood

- We made some under the hood changes. ([PR #<num>](...), [PR #<num>](...))
```

- Top header: `# <version> - <release date>`; date is `+7 days` from this Wednesday's prerelease (next Wednesday's stable release), written by the `write-changelog` job, not changelog-body
- Sections, in order: `Added`, `Fixed`, `Changed`, `Under the Hood`. Section is an AI judgment call per `.cursor/skills/changelog-judgment/SKILL.md` (`Added` = new capability, `Fixed` = bugfix, `Changed` = behavior change, `Under the Hood` = invisible to users), not a fixed commit-type mapping — see `scripts/changelogBody/changelogBody.mts`
- Kept commit types: `feat`, `fix`, `perf` (everything else, e.g. `chore`/`refactor`/`test`/`ci`, is dropped before the AI step ever sees it)
- `Under the Hood` entries are consolidated into one bullet with all their PR links, no package sub-header
- Package sections: `#### <package-name>`, alphabetical within a type
- Bullet entries: `- <message> ([PR #N](url))`; PR url format `https://github.com/forcedotcom/salesforcedx-vscode/pull/<num>`
- Multiple PRs for one entry: comma-separate `([PR #A](...), [PR #B](...), [ISSUE #C](...), [DISCUSSION #D](...))`
- Include `[ISSUE #N](https://github.com/forcedotcom/salesforcedx-vscode/issues/N)` or `[DISCUSSION #N](https://github.com/forcedotcom/salesforcedx-vscode/discussions/N)` when listed in the PR's "What issues does this PR fix or reference?" section

### Package filtering (auto)

- If `salesforcedx-vscode-core` is touched, all other touched packages (except `docs`) are dropped for that commit
- If `salesforcedx-vscode-services` is touched alongside exactly one other (non-`docs`) package, `salesforcedx-vscode-services` is dropped and the other package keeps the entry
- Only packages starting with `salesforce` or `docs` count; path segments named `images` or `test` are ignored when detecting touched packages
- A commit whose surviving paths touch no qualifying package has an empty package set and goes to `Under the Hood`

### Commit parsing (auto)

- Requires conventional `type(scope): message` + trailing `(#PR)` to appear in the commit subject
- Strips `[W-XXXXXXXX]` GUS refs from the subject before it reaches the AI step
- No dedupe against the existing changelog file — the range is disjoint by construction (see Commit details above)

### Dedupe / merge rules (post-generation)

- Same PR listed under multiple packages: keep under the most user-relevant package, delete others (see Rule 3 above)
- Same PR listed twice in a section: collapse to one bullet
- Empty `#### <package>` subsections: leave in place if auto-generated (keeps structure obvious), or remove during polish — match surrounding style
- Patch release rolling into next: if v66.Y.A shipped but entries were missing, merge into v66.Y.B section and update the header date; see `chore: merge ... changelogs`
