Files
msd-core/.changeset
Tom Boucher 6e681cb36c fix(#3901): commit-docs-guard suites isolate children from a global core.hooksPath (#4063)
* test(#3901): the guard suite must survive a developer's global core.hooksPath (failing first)

* fix(#3901): the commit-docs-guard suite isolates children from the host git config

A developer's GLOBAL core.hooksPath (~/.gitconfig) applies to every
fresh repo, so the guard's correct refusal — it will not install a hook
git would never run — failed this suite's beforeEach and 18 tests read
as a commit-docs-guard regression on any machine that centralizes
commit hooks. CI never saw it (runners have no global git config).

before() pins GIT_CONFIG_GLOBAL to an empty file for the whole suite
(runGsdTools and the git helpers propagate process.env to every child).
A LOCAL repo value cannot isolate this: any non-empty local value trips
the same refusal, and an empty local value makes rev-parse --git-path
hooks resolve to ./ instead of .git/hooks. The regression test pins the
seam: the isolation file is armed and empty, a child git resolves no
hooksPath from the host, and the beforeEach enable installed the hook
at the repo-local default path. A child given an explicitly hostile
GIT_CONFIG_GLOBAL still refuses — that is the guard being correct.

* fix(#3901): review fold-ins — isolation hoisted to all three guard suites; changeset

Adversarial review: the pin covered suite A only — the B (#3588 B1-B15)
and D (real git commit wiring) suites run enable against fresh repos
too and failed identically on hostile-global machines; the issue's 18
failures could not have come from A's five tests alone. The pin is
extracted to isolateGlobalGitConfig() and armed in all three suites
(B9's LOCAL hooksPath test is unaffected — the pin neutralizes only the
global scope; D's premise is the hook firing from .git/hooks, which the
isolation preserves).

* chore(#3901): backfill changeset PR number (4063)

* fix(#3901): drop the now-unused before import — max-warnings 0 fails CI

---------

Co-authored-by: sim <sim@local>
2026-08-29 18:20:14 -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

Promotion is automatic. The release workflow's finalize job runs:

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

This reads every fragment, groups bullets by type:, replaces ## [Unreleased] with a new ## [vX.Y.Z] - YYYY-MM-DD block, opens a fresh ## [Unreleased] above, and deletes consumed fragments. The --allow-empty flag ensures a no-change release still gets a dated heading (with a _No notable changes._ placeholder). A subsequent verify step confirms the promotion landed correctly. Maintainers do not run this by hand.

Archived fragments

.changeset/archived/ holds fragments for already-shipped releases (≤ 1.3.1), retained for provenance. Their content was hand-curated into the dated ## [1.x.y] sections of CHANGELOG.md during the #690 backfill — they were never consumed by render. All changeset tooling enumerates .changeset/ non-recursively, so archived fragments are never picked up or rendered. Do not move them back to the top level.