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

@@ -3,21 +3,21 @@
/**
* loop-walk.cjs — a small stateful harness around `tests/helpers.cjs` for the
* loop QA walk. This module owns NONE of the subprocess mechanics itself
* (spawn, retry-on-kill, quote-splitting) — those live in `runGsdTools` /
* (spawn, retry-on-kill, quote-splitting) — those live in `runMsdTools` /
* `createTempGitProject` and are reused verbatim. What this module adds is
* the walk-specific concerns: turning a raw helper result into the typed
* `RunResult` from `./result.cjs`, guaranteeing the child never inherits an
* ambient `GSD_*` variable that would silently redirect the engine, pinning
* ambient `MSD_*` variable that would silently redirect the engine, pinning
* the clock so output is reproducible run-to-run, and giving callers a
* content-free way to observe what the SUT wrote to disk.
*/
const fs = require('fs');
const path = require('path');
const { runGsdTools, cleanup } = require('../helpers.cjs');
const { runMsdTools, cleanup } = require('../helpers.cjs');
const { classify } = require('./result.cjs');
const { createFixture } = require('../fixtures/index.cjs');
const { LOOP_HOST_CONTRACT } = require('../../gsd-core/bin/lib/loop-host-contract.cjs');
const { LOOP_HOST_CONTRACT } = require('../../msd-core/bin/lib/loop-host-contract.cjs');
const { resolveWithin } = require('./paths.cjs');
/**
@@ -48,19 +48,19 @@ const DEFAULT_NOW_MS = 1767225600000;
* only passes `git: true` silently also gets `.planning/PROJECT.md` seeded.
* A "greenfield" walk built that way would report `project_exists: true`
* from its very first step, which makes the greenfield trajectory
* meaningless: it can never exercise the create path (`/gsd-new-project`)
* meaningless: it can never exercise the create path (`/msd-new-project`)
* because a project already exists before the walk begins. Every entry
* below therefore states `projectDoc` explicitly rather than relying on
* that default, and `greenfield` is the harness default precisely because
* an empty git repo with no `.planning/` at all is what a real user's
* working tree looks like before running `/gsd-new-project` for the first
* working tree looks like before running `/msd-new-project` for the first
* time.
*
* @type {Record<string, (prefix: string) => string>}
*/
const FIXTURE_BUILDERS = {
// A git repo with NO `.planning/` at all — what a user has before
// `/gsd-new-project`.
// `/msd-new-project`.
greenfield: (prefix) => createFixture({ prefix, git: true, planning: false, projectDoc: false }),
// `.planning/phases/` exists, but no PROJECT.md yet.
planning: (prefix) => createFixture({ prefix, git: true, planning: true, projectDoc: false }),
@@ -117,7 +117,7 @@ function collectStatSnapshot(root) {
class LoopWalk {
/**
* @param {string} dir - absolute project root (an already-created temp git project).
* @param {number} nowMs - pinned epoch ms passed to every `run()` as `GSD_NOW_MS`.
* @param {number} nowMs - pinned epoch ms passed to every `run()` as `MSD_NOW_MS`.
*/
constructor(dir, nowMs) {
this.dir = dir;
@@ -150,7 +150,7 @@ class LoopWalk {
* @returns {LoopWalk}
*/
static create(opts = {}) {
const { prefix = 'gsd-loop-walk-', nowMs = DEFAULT_NOW_MS, fixture = 'greenfield' } = opts;
const { prefix = 'msd-loop-walk-', nowMs = DEFAULT_NOW_MS, fixture = 'greenfield' } = opts;
const build = FIXTURE_BUILDERS[fixture];
if (!build) {
throw new Error(
@@ -162,7 +162,7 @@ class LoopWalk {
}
/**
* Run a `gsd-tools` invocation inside this walk's project and return the
* Run a `msd-tools` invocation inside this walk's project and return the
* typed `RunResult` from `classify()`.
*
* SIGNATURE: `run(...argvTokens)` where the LAST argument, if it is a
@@ -176,7 +176,7 @@ class LoopWalk {
*
* WHY `jsonErrors` defaults to `true`: `--json-errors` (`docs/json-errors.md`)
* is a real CLI flag, but it is the TOOLING/TEST surface — a human or a real
* workflow invokes `gsd_run <cmd>` directly, WITHOUT it. Defaulting to
* workflow invokes `msd_run <cmd>` directly, WITHOUT it. Defaulting to
* `true` keeps every pre-existing call site's behavior byte-for-byte
* unchanged (they all exercised `--json-errors` before this option
* existed), while `{ jsonErrors: false }` lets a scenario step opt into
@@ -184,16 +184,16 @@ class LoopWalk {
* prove the tooling surface works and could never catch a regression a
* real user would actually hit.
*
* WHY the env is built the way it is (ambient `GSD_*` sanitization):
* `runGsdTools` composes the child env as
* `{ ...process.env, ...TEST_ENV_BASE, ...env }` — so whatever `GSD_*`
* WHY the env is built the way it is (ambient `MSD_*` sanitization):
* `runMsdTools` composes the child env as
* `{ ...process.env, ...TEST_ENV_BASE, ...env }` — so whatever `MSD_*`
* variables happen to be set in the *parent* shell (e.g. a developer or CI
* runner with `GSD_WORKSTREAM` / `GSD_PROJECT` exported for an unrelated
* runner with `MSD_WORKSTREAM` / `MSD_PROJECT` exported for an unrelated
* reason) flow straight through into the child and can silently redirect
* the engine at a different workstream or project root than the one this
* walk created — an invisible, non-deterministic test-pollution vector.
* `runGsdTools`'s own merge order means the last object spread wins, so
* this method builds an `env` override that sets EVERY ambient `GSD_*` key
* `runMsdTools`'s own merge order means the last object spread wins, so
* this method builds an `env` override that sets EVERY ambient `MSD_*` key
* (scanned live from `process.env`, not a hardcoded list — a new leaking
* var needs no code change here to be caught) to `undefined`, then layers
* the two intentionally-pinned vars on top.
@@ -203,29 +203,29 @@ class LoopWalk {
* exercised here via `execFileSync`) iterates `Object.keys(env)` and
* OMITS any key whose value is `undefined` from the actual `KEY=VALUE`
* pairs handed to the OS — it does not stringify it to the literal text
* `"undefined"`. That means `{ GSD_WORKSTREAM: undefined }` in the `env`
* option makes the child process behave exactly as if `GSD_WORKSTREAM`
* were never exported at all, even though `process.env.GSD_WORKSTREAM` is
* `"undefined"`. That means `{ MSD_WORKSTREAM: undefined }` in the `env`
* option makes the child process behave exactly as if `MSD_WORKSTREAM`
* were never exported at all, even though `process.env.MSD_WORKSTREAM` is
* still set and non-empty in the parent. This was verified empirically
* (not assumed) — see the module verification transcript — because
* `delete`-based approaches were not available here (the merge is inside
* `runGsdTools`, not under this method's control) and a stringified
* `runMsdTools`, not under this method's control) and a stringified
* `"undefined"` would have been a silent correctness bug indistinguishable
* from a passing run until an actual leak test caught it.
*
* ⚠️ KNOWN LIMIT — SUCCESS-PATH STDERR IS NOT OBSERVABLE THROUGH THIS
* SUBSTRATE, SO `result.warnings` IS ERROR-PATH-ONLY TODAY: this method
* builds `raw.stderr` as `result.success ? '' : (result.error ?? '')`
* (below), and `runGsdTools` (`tests/helpers.cjs`) invokes the child via
* (below), and `runMsdTools` (`tests/helpers.cjs`) invokes the child via
* `execFileSync`, which discards the child's stderr stream entirely on a
* clean (non-throwing) exit — Node never captures it, so there is no text
* to forward even if this method wanted to. The practical effect: for any
* exit-0 invocation, `classify()` always receives `stderr: ''`, so
* `result.warnings` can never be non-empty on the success path, no matter
* what the real `gsd-tools` process actually wrote to stderr. `warnings`
* what the real `msd-tools` process actually wrote to stderr. `warnings`
* only ever populates on the exit-1 (error) path, where `result.error`
* (helpers.cjs's captured stderr-on-failure text) is threaded through.
* Capturing success-path stderr would require changing `runGsdTools` /
* Capturing success-path stderr would require changing `runMsdTools` /
* `tests/helpers.cjs` (e.g. to `spawnSync`) — out of scope here because
* that helper is shared by ~131 test files. DO NOT build an oracle that
* assumes `.warnings` reflects success-path stderr; it structurally cannot
@@ -246,17 +246,17 @@ class LoopWalk {
/** @type {Record<string, string|undefined>} */
const sanitize = {};
for (const key of Object.keys(process.env)) {
if (key.startsWith('GSD_')) sanitize[key] = undefined;
if (key.startsWith('MSD_')) sanitize[key] = undefined;
}
const env = {
...sanitize,
GSD_TEST_MODE: '1',
GSD_NOW_MS: String(this.nowMs),
MSD_TEST_MODE: '1',
MSD_NOW_MS: String(this.nowMs),
};
let raw;
try {
const result = runGsdTools(argv, this.dir, env);
const result = runMsdTools(argv, this.dir, env);
raw = {
exitCode: result.exitCode,
stdout: result.output,
@@ -265,7 +265,7 @@ class LoopWalk {
argv,
};
} catch {
// `runGsdTools` throws only after a retried, persistent subprocess
// `runMsdTools` throws only after a retried, persistent subprocess
// kill (host OOM / scheduler contention — see helpers.cjs
// `throwResourceStarvation`). That is a statement about the HOST, not
// the engine under test, so a walk must degrade to a TIMEOUT result
@@ -308,7 +308,7 @@ class LoopWalk {
* Remove this walk's temp project. Safe to call multiple times.
*
* `opts.keep` (default `false`) skips the removal entirely — the caller
* (a QA-report run, typically via `--keep` / `GSD_QA_KEEP=1`) wants the
* (a QA-report run, typically via `--keep` / `MSD_QA_KEEP=1`) wants the
* failing/inspected tree left on disk for a human to `cd` into. When kept,
* this returns `this.dir` so the caller can record it (e.g. as
* `preservedDir` on a scenario report); when actually cleaned up, it