Files
msd-core/.changeset
Tom Boucher 48c1827028 enhancement(#40): integrate branch pruning into /gsd-cleanup archival workflow (#564)
* enhancement(#40): integrate branch pruning into /gsd-cleanup archival workflow

Adds a prune_local_branches step to cleanup.md (between archive_phases and
commit) that force-deletes local branches whose upstream is gone — keeping
local clones symmetric with delete_branch_on_merge on GitHub.

Key design choices vs. PR #562 (the local-model draft):
- dry-run step shows stale branches using cached tracking refs only; git
  fetch --prune is deferred to the execution step so the dry-run is
  non-side-effecting
- awk uses { if ($1 != "*") print $1 } form to explicitly exclude the
  currently checked-out branch (the * prefix in git branch -vv output),
  not a prose note that lets xargs receive literal * as an argument
- git fetch --prune runs exactly once, in prune_local_branches, eliminating
  the TOCTOU window between a preview fetch and an execution fetch
- two new negative-contract tests: identify_completed_milestones must not
  run git branch commands; show_dry_run must not run git fetch --prune

Closes #40

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: regenerate changeset using repo script (correct format)

Replaces hand-written fragment (used `/** ... */` comment syntax)
with one generated by `npm run changeset -- --type Changed --pr 562`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address codex review blockers — protect main/next/trunk, align dry-run with execution

Codex adversarial review (pre-PR gate) flagged two blockers:

1. awk filter only excluded '*' (current branch) but not protected names.
   main/next/trunk/develop could be force-deleted if their upstream was
   gone. Fix: use !~ /^\*$|^main$|^next$|^trunk$|^develop$/ regex match.

2. Dry-run enumerated from cached tracking refs; execution re-ran
   git fetch --prune, creating a TOCTOU window between what the user
   confirmed and what got deleted. Fix: move git fetch --prune into
   show_dry_run (prefetch for display accuracy); prune_local_branches
   now enumerates from the already-fetched state with no second fetch.

Updated 14 structural tests to match new design (added protected-name
exclusion test; inverted show_dry_run fetch assertion).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 22:31:54 -04:00
..

Changeset Fragments

This directory holds per-PR CHANGELOG fragments. Every PR with user-facing changes drops one (or more) <random-name>.md files here describing its CHANGELOG entry. Fragments are consolidated into the top-level CHANGELOG.md at release time.

Why

Two PRs that both edit the ### Fixed block of CHANGELOG.md always conflict on merge — git can't pick a serialization order without human input. Two PRs that each add a fresh .changeset/<unique-name>.md never conflict because they don't share lines.

See #2975 for the full rationale.

Adding a fragment

node scripts/changeset/new.cjs \
  --type Fixed \
  --pr 1234 \
  --body "fix the thing — explain the user-visible change in one sentence"

This writes .changeset/<adjective>-<noun>-<noun>.md with frontmatter and a body. Three random words → concurrent PRs don't collide.

Format

---
type: Fixed
pr: 1234
---
**`/gsd-foo` no longer drops trailing slashes** — explain the user-visible change.

Allowed type: values follow Keep a Changelog: Added, Changed, Deprecated, Removed, Fixed, Security.

Opting out

PRs that legitimately have no user-facing impact can add the no-changelog label. CI honors it. When unsure, add the fragment.

At release time

node scripts/changeset/cli.cjs render --version vX.Y.Z --date YYYY-MM-DD

Reads every fragment, groups bullets by type:, replaces ## [Unreleased] with a new ## [vX.Y.Z] - YYYY-MM-DD block, opens a fresh ## [Unreleased] above, deletes consumed fragments. Idempotent.