* 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>
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.