refactor: hard-fork GSD -> MSD (Make Software Done)

Mechanical rename produced by scripts/msd-rename.cjs: gsd/Gsd/GSD -> msd/Msd/MSD
across contents and paths, upstream package/repo coordinates -> @golem15/msd-core
and golem15com/msd-core. Deep links into upstream history, sibling upstream
packages, the GSD-2 import feature, CHANGELOG.md and .changeset/ are kept as-is.

Hand edits on top: MSD block-letter banner and logos, LICENSE copyright line,
package/plugin identity, regenerated lockfile, install-tree fixtures, derived
registries and benchmark baseline; migration checksum baseline re-locked
(MSD keeps its own install state, so no install had applied the old sums);
sort-order and regex-escaped expectations in tests adjusted.
This commit is contained in:
Jakub Zych
2026-10-06 01:47:40 +02:00
parent fe069b2a56
commit a9a7a328e6
2763 changed files with 78465 additions and 78434 deletions

View File

@@ -139,12 +139,12 @@ type WorkstreamAdapterOpts = Record<string, unknown>;
* two-readers-two-bases lesson).
*/
function resolveEnvWorkstream(): string | null {
const value = process.env['GSD_WORKSTREAM']?.trim();
const value = process.env['MSD_WORKSTREAM']?.trim();
return value || null;
}
function planningDir(cwd: string, ws?: string | null, project?: string | null): string {
if (project === undefined) project = process.env['GSD_PROJECT']?.trim() || null;
if (project === undefined) project = process.env['MSD_PROJECT']?.trim() || null;
else if (typeof project === 'string') project = project.trim() || null;
if (ws === undefined) ws = resolveEnvWorkstream();
else if (typeof ws === 'string') ws = ws.trim() || null;
@@ -152,10 +152,10 @@ function planningDir(cwd: string, ws?: string | null, project?: string | null):
// Reject path separators and traversal components in project/workstream names
const BAD_SEGMENT = /[/\\]|\.\./;
if (project && BAD_SEGMENT.test(project)) {
throw new Error(`GSD_PROJECT contains invalid path characters: ${project}`);
throw new Error(`MSD_PROJECT contains invalid path characters: ${project}`);
}
if (ws && BAD_SEGMENT.test(ws)) {
throw new Error(`GSD_WORKSTREAM contains invalid path characters: ${ws}`);
throw new Error(`MSD_WORKSTREAM contains invalid path characters: ${ws}`);
}
let base = path.join(cwd, '.planning');
@@ -174,8 +174,8 @@ function planningRoot(cwd: string): string {
* isolation-deciding surface must share (config-get's merged view is the
* contract). Ladder: the scoped config's OWN key wins (planningDir is
* project- and workstream-aware); otherwise the flat root's key, but only
* under the GSD_WORKSTREAM env gate — config-get deliberately does NOT
* inherit root under GSD_PROJECT alone, and this read must not diverge
* under the MSD_WORKSTREAM env gate — config-get deliberately does NOT
* inherit root under MSD_PROJECT alone, and this read must not diverge
* (#3963). Strict `=== false` (never coerced); any read failure degrades to
* "not opted out" (worktrees on — the fail-safe direction: the guard keeps
* enforcing). Direct file reads only — never loadConfig, which normalizes
@@ -183,7 +183,7 @@ function planningRoot(cwd: string): string {
*/
function worktreesOptedOut(cwd: string): boolean {
// #3972 review: the WHOLE body is guarded — planningDir/planningRoot
// themselves throw on a GSD_PROJECT/GSD_WORKSTREAM value containing path
// themselves throw on a MSD_PROJECT/MSD_WORKSTREAM value containing path
// separators or `..`, and this contract ("any failure degrades to not
// opted out — worktrees on, keep enforcing") must hold for that shape too.
try {
@@ -245,7 +245,7 @@ function worktreesOptedOutUnguarded(cwd: string): boolean {
* either missing from disk or malformed on disk. One resolver, one answer.
*
* The workstream is `planningDir`'s own `ws` parameter, forwarded, so this
* shares the canonical resolution (and its GSD_PROJECT/GSD_WORKSTREAM
* shares the canonical resolution (and its MSD_PROJECT/MSD_WORKSTREAM
* handling). Root is consulted as a fallback only when a workstream is active,
* matching config-loader; a project-scoped directory stands alone. Returns null
* when unset, absent, or unreadable — every caller treats null as "not the
@@ -255,14 +255,14 @@ function worktreesOptedOutUnguarded(cwd: string): boolean {
* here. It was omitted at first on the reasoning that "the active workstream is
* whatever planningDir resolves" — true only for the env-driven caller. A
* caller that iterates workstreams passes the name as an ARGUMENT (it cannot
* set `GSD_WORKSTREAM` per iteration), and `planningDir` falls back to the env
* set `MSD_WORKSTREAM` per iteration), and `planningDir` falls back to the env
* only when `ws` is `undefined`, so an argument-driven call resolved this
* convention from the ROOT config while reading that workstream's ROADMAP. Two
* consequences, both reproduced: a workstream that explicitly declares its OWN
* convention had it ignored — the root's value decided how the workstream's
* roadmap was parsed, so flipping ONLY the root config changed which milestone
* a workstream extracted; and `--workstream foo` disagreed with
* `GSD_WORKSTREAM=foo` on the same repo.
* `MSD_WORKSTREAM=foo` on the same repo.
*
* `undefined` (the default) keeps `planningDir`'s env fallback, so every
* pre-#2761 call site is byte-identical; `null` means "explicitly no
@@ -624,7 +624,7 @@ function describeUnresolvedWorkstreamReason(reason: 'invalid_name' | 'missing_wo
/**
* Locate the CONTEXT.md file in a phase directory, handling both the bare
* form (`CONTEXT.md`) and the padded-prefix convention (`NN-CONTEXT.md`,
* `NN.N-CONTEXT.md`, etc.) used by gsd-discuss-phase output.
* `NN.N-CONTEXT.md`, etc.) used by msd-discuss-phase output.
*
* Canonical dual-form predicate extracted here to eliminate the 5-site
* duplication that previously existed across init.cjs, roadmap.cjs,