* test: reproduce CJS dispatcher rejecting dotted form (#3243) runGsdTools assertions confirm that generate-slug.hello-world, current-timestamp.date, validate.plan, roadmap.analyze, phases.list, and check.decision-coverage-plan all fail with "Unknown command: <dotted>" — the dispatcher switch only accepts the spaced form. Edge cases (no dots unchanged, leading-dot rejected, unknown dotted form suggests spaced equivalent) are also specified; those three pass already because the shim is not yet implemented. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(cjs): accept dotted canonical command form (#3243) Add a shim at the top of main() in gsd-tools.cjs that splits args[0] on the first dot when present, normalizing "state.update" → command='state' args=['state','update',...] before the switch statement is reached. Any caller that bypasses the SDK (stale npm-installed binary, workflow shell-out, third-party script) can now use the canonical dotted form natively without hitting "Unknown command: <domain>.<subcommand>". The shim guards against empty head/rest so ".hidden" and bare "." args are unchanged and fall through to the existing "Unknown command" path. Also improves the default "Unknown command" error message to suggest the spaced equivalent when a dotted form was passed — e.g. for "foo.bar" the error now reads: Unknown command: foo — did you mean: "foo bar"? Parallel to dottedCommandToCjsArgv in sdk/src/query/query-fallback-bridge-adapter.ts; intentionally kept separate to avoid SDK coupling. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * changeset: pr=3248 for #3243 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: tighten dotted-form suggestion assertion (CR nitpick) * fix(cjs): suggestion uses first-dot split (CR finding 1, multi-dot consistency) The "did you mean" hint in the Unknown-command default case was replacing ALL dots with spaces (state.update.foo → "state update foo"), but the dispatcher shim only splits on the FIRST dot (state.update.foo → head=state, rest=update.foo). Apply CR's exact patch to use indexOf+slice so suggestion matches dispatch behavior. Add a multi-dot regression test (a.b.c must suggest "a b.c", not "a b c"). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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.