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

@@ -4,7 +4,7 @@
* Shared helper for the canonical shipped-agent roster.
*
* Several tests derive "the set of agents we ship" from the source `agents/`
* directory via `fs.readdirSync(...).filter(/^gsd-.*\.md$/)`. This consolidates
* directory via `fs.readdirSync(...).filter(/^msd-.*\.md$/)`. This consolidates
* that hand-duplicated logic into one canonical SOURCE-roster derivation.
*
* NOTE: This returns the SOURCE roster (basenames without `.md`, sorted). Sites
@@ -16,7 +16,7 @@
* existing roster entry, served by the `#2454` fallback in `cmdAgentSkills`
* (`src/init.cts`) when `workflow.compact_content` is on. Every roster-identity
* check this helper backs (frontmatter/tools invariants, "every shipped agent"
* install assertions, etc.) is about the SET of distinct agents GSD ships, which
* install assertions, etc.) is about the SET of distinct agents MSD ships, which
* a variant does not add to.
*/
@@ -32,12 +32,12 @@ const AGENTS_DIR = path.join(__dirname, '..', '..', 'agents');
*
* @param {string} [agentsDir] Override for the source agents directory.
* Defaults to the canonical `<repo-root>/agents`.
* @returns {string[]} Sorted `gsd-*` basenames with `.md` stripped.
* @returns {string[]} Sorted `msd-*` basenames with `.md` stripped.
*/
function listAgentFiles(agentsDir = AGENTS_DIR) {
return fs
.readdirSync(agentsDir)
.filter((f) => /^gsd-.*\.md$/.test(f) && !f.endsWith('.compact.md'))
.filter((f) => /^msd-.*\.md$/.test(f) && !f.endsWith('.compact.md'))
.map((f) => f.replace(/\.md$/, ''))
.sort();
}

View File

@@ -1,7 +1,7 @@
/**
* CLI negative-matrix harness (#3593).
*
* Wraps spawnSync of gsd-core/bin/gsd-tools.cjs so test files can
* Wraps spawnSync of msd-core/bin/msd-tools.cjs so test files can
* assert on structured outputs (exit code, typed reason, stack-trace
* absence) without each test re-implementing the JSON-errors parsing
* dance. Hostile values are passed as argv elements — never composed
@@ -29,12 +29,12 @@
const path = require('node:path');
const { spawnSync } = require('node:child_process');
const TOOLS_PATH = path.resolve(__dirname, '..', '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
const TOOLS_PATH = path.resolve(__dirname, '..', '..', 'msd-core', 'bin', 'msd-tools.cjs');
/**
* Run gsd-tools with the given argv against a project directory.
* Run msd-tools with the given argv against a project directory.
*
* @param {string[]} argv - argument vector passed to gsd-tools.cjs. Each
* @param {string[]} argv - argument vector passed to msd-tools.cjs. Each
* element reaches the child as a single argv element regardless of
* shell metacharacters in its value.
* @param {object} options

View File

@@ -2,21 +2,21 @@
/**
* Build a hermetic "cold tree" install fixture for #3582 — a copy of hooks/
* plus gsd-core/bin/ensure-runtime-build.cjs with the compiled
* gsd-core/bin/lib/*.cjs directory and tsconfig.build.json deliberately
* plus msd-core/bin/ensure-runtime-build.cjs with the compiled
* msd-core/bin/lib/*.cjs directory and tsconfig.build.json deliberately
* ABSENT, mirroring a raw plugin-marketplace / git-clone install that never
* ran `npm run build:lib`.
*
* Deliberately NOT `tests/helpers/copy-script-fixture.cjs`'s
* `copyScriptWithDeps`: that helper walks the require graph and copies every
* dependency it finds — including gsd-core/bin/lib/*.cjs, which exist in
* dependency it finds — including msd-core/bin/lib/*.cjs, which exist in
* THIS repo's already-built tree — so it would faithfully reproduce a WARM
* tree, the opposite of what a cold-tree test needs. This helper copies only
* hooks/ and the seam module itself, and never touches gsd-core/bin/lib/ or
* hooks/ and the seam module itself, and never touches msd-core/bin/lib/ or
* tsconfig.build.json — so `ensureRuntimeBuild()` inside the fixture
* deterministically throws `RuntimeBuildError` ("tsconfig.build.json not
* found") the first time any fixture hook reaches it, without ever deleting
* or touching the real repo's gsd-core/bin/lib/.
* or touching the real repo's msd-core/bin/lib/.
*/
const fs = require('node:fs');
@@ -39,7 +39,7 @@ function shouldCopyHookEntry(name) {
/**
* @param {object} [opts]
* @param {string} [opts.repoRoot] TEST-ONLY: source tree root to copy hooks/
* and gsd-core/bin/ensure-runtime-build.cjs from, in place of the real
* and msd-core/bin/ensure-runtime-build.cjs from, in place of the real
* REPO_ROOT. Lets a test point this fixture at a hermetic fake tree instead
* of mutating the live repo's hooks/ directory (which other test files may
* be concurrently reading). Defaults to REPO_ROOT.
@@ -47,7 +47,7 @@ function shouldCopyHookEntry(name) {
*/
function buildColdInstallTree(opts = {}) {
const repoRoot = opts.repoRoot || REPO_ROOT;
const dir = fs.mkdtempSync(path.join(require('node:os').tmpdir(), 'gsd-cold-tree-'));
const dir = fs.mkdtempSync(path.join(require('node:os').tmpdir(), 'msd-cold-tree-'));
// hooks/ — entire directory (top-level hook scripts + hooks/lib/*.js +
// managed-hooks-registry.cjs + hooks.json). hooks/dist/ (gitignored,
@@ -86,14 +86,14 @@ function buildColdInstallTree(opts = {}) {
});
}
// gsd-core/bin/ensure-runtime-build.cjs — the seam itself. Deliberately
// NOT gsd-core/bin/lib/ (absent — isBuilt() reads false) and NOT
// msd-core/bin/ensure-runtime-build.cjs — the seam itself. Deliberately
// NOT msd-core/bin/lib/ (absent — isBuilt() reads false) and NOT
// tsconfig.build.json at the fixture root (absent — ensureRuntimeBuild's
// "cannot auto-build" branch fires deterministically).
fs.mkdirSync(path.join(dir, 'gsd-core', 'bin'), { recursive: true });
fs.mkdirSync(path.join(dir, 'msd-core', 'bin'), { recursive: true });
fs.copyFileSync(
path.join(repoRoot, 'gsd-core', 'bin', 'ensure-runtime-build.cjs'),
path.join(dir, 'gsd-core', 'bin', 'ensure-runtime-build.cjs'),
path.join(repoRoot, 'msd-core', 'bin', 'ensure-runtime-build.cjs'),
path.join(dir, 'msd-core', 'bin', 'ensure-runtime-build.cjs'),
);
function cleanup() {

View File

@@ -6,8 +6,8 @@
* spec this module implements — it is the source of truth for behavior; this
* file is the mechanics.
*
* A "registered split" is any `gsd-core/workflows/<name>/detail/*.md` path
* paired with the spine at `gsd-core/workflows/<name>.md`. There is no
* A "registered split" is any `msd-core/workflows/<name>/detail/*.md` path
* paired with the spine at `msd-core/workflows/<name>.md`. There is no
* separate registry — a pair is registered by existing on disk
* (`discoverRegisteredSplits`). Everything else here is one of the five
* checks PARTITION-RULES.md describes, or a primitive those checks share:
@@ -38,8 +38,8 @@ const {
const { NEW_FILE_CAP, ACK_TRAILER_DELIM, normalizeAckReason } = require('./emitted-diff.cjs');
const { runGit, OUTCOME } = require('./process-seam.cjs');
/** Default scan root: `gsd-core/workflows/` at repo root. */
const DEFAULT_WORKFLOWS_DIR = path.join(__dirname, '..', '..', 'gsd-core', 'workflows');
/** Default scan root: `msd-core/workflows/` at repo root. */
const DEFAULT_WORKFLOWS_DIR = path.join(__dirname, '..', '..', 'msd-core', 'workflows');
/**
* Discover every registered spine/detail split under `workflowsDir`.
@@ -173,13 +173,13 @@ const KNOWN_BOILERPLATE_PREFIXES = [
/**
* Is `line` one of the corpus's known sanctioned cross-file duplicates: the
* canonical `gsd_run` launcher bootstrap preamble (see
* `gsd-core/workflows/_runtime-launcher.snippet.sh`,
* canonical `msd_run` launcher bootstrap preamble (see
* `msd-core/workflows/_runtime-launcher.snippet.sh`,
* `tests/runtime-launcher-parity.test.cjs`), or one of the `KNOWN_BOILERPLATE_PREFIXES`
* above?
*
* The launcher preamble's own guard mandates exactly one inlined copy in every
* workflow/detail file that calls `gsd_run` — spine and detail both call it,
* workflow/detail file that calls `msd_run` — spine and detail both call it,
* so both legitimately carry their own copy. That is sanctioned
* cross-file duplication, not something the disjointness check should ever
* flag, so it is filtered out wherever this module compares lines across a
@@ -189,17 +189,17 @@ const KNOWN_BOILERPLATE_PREFIXES = [
* @returns {boolean}
*/
function isKnownSanctionedBoilerplate(line) {
if (line.startsWith('_GSD_SHIM_NAME="gsd-tools.cjs";')) return true;
if (line.startsWith('_MSD_SHIM_NAME="msd-tools.cjs";')) return true;
return KNOWN_BOILERPLATE_PREFIXES.some((prefix) => line.startsWith(prefix));
}
const PROTECTED_START = '<!-- gsd:protected:start -->';
const PROTECTED_END = '<!-- gsd:protected:end -->';
const PROTECTED_SINGLE = '<!-- gsd:protected -->';
const PROTECTED_START = '<!-- msd:protected:start -->';
const PROTECTED_END = '<!-- msd:protected:end -->';
const PROTECTED_SINGLE = '<!-- msd:protected -->';
/**
* Extract every `<!-- gsd:protected -->` (single-line) and
* `<!-- gsd:protected:start -->`/`<!-- gsd:protected:end -->` (paired)
* Extract every `<!-- msd:protected -->` (single-line) and
* `<!-- msd:protected:start -->`/`<!-- msd:protected:end -->` (paired)
* sentinel-wrapped block from `content`.
*
* Never throws on a malformed sentinel (unclosed start, orphan end, a single
@@ -207,7 +207,7 @@ const PROTECTED_SINGLE = '<!-- gsd:protected -->';
* those are reported best-effort in `blocks` (covering whatever content is
* actually present, or an empty span) AND named explicitly in `malformed`,
* mirroring the well-formedness rules `tests/plan-phase-compact-split.test.cjs`'s
* `'every gsd:protected sentinel...'` test already enforced for the one
* `'every msd:protected sentinel...'` test already enforced for the one
* existing split. The check-4 caller decides what to do with a malformed
* sentinel; this function's job is only to describe the file accurately.
*
@@ -236,12 +236,12 @@ function extractProtectedBlocks(content) {
// A new start while one is already open: the earlier open span is
// abandoned (best-effort — it never gets a `blocks` entry) and this
// one becomes the tracked open start.
malformed.push({ line: i + 1, message: `nested/unclosed gsd:protected:start at line ${i + 1}` });
malformed.push({ line: i + 1, message: `nested/unclosed msd:protected:start at line ${i + 1}` });
}
openStart = i;
} else if (trimmed === PROTECTED_END) {
if (openStart === -1) {
malformed.push({ line: i + 1, message: `gsd:protected:end with no matching start at line ${i + 1}` });
malformed.push({ line: i + 1, message: `msd:protected:end with no matching start at line ${i + 1}` });
continue;
}
const covered = lines.slice(openStart + 1, i).map((l) => l.trim()).filter((l) => l.length > 0);
@@ -259,7 +259,7 @@ function extractProtectedBlocks(content) {
}
if (openStart !== -1) {
malformed.push({ line: openStart + 1, message: 'a gsd:protected:start sentinel was never closed' });
malformed.push({ line: openStart + 1, message: 'a msd:protected:start sentinel was never closed' });
const covered = lines.slice(openStart + 1).map((l) => l.trim()).filter((l) => l.length > 0);
blocks.push({ kind: 'paired', lines: covered, firstLine: covered[0] || '' });
}
@@ -446,7 +446,7 @@ function readBoundaryMoveTrailers({ baseRef, headRef = 'HEAD', cwd = REPO_ROOT,
* what a given PR touched — this is what keeps duplication from creeping
* back in after a split is made.
*
* The canonical `gsd_run` launcher preamble is excluded from both sides
* The canonical `msd_run` launcher preamble is excluded from both sides
* before comparing (`isKnownSanctionedBoilerplate`) — it is sanctioned
* cross-file duplication under a different guard's contract, not a
* violation of this one.
@@ -526,12 +526,12 @@ function checkRegistration(splits, workflowsDir) {
}
}
// The prose spells these out in full — e.g. `gsd-core/workflows/plan-phase/
// The prose spells these out in full — e.g. `msd-core/workflows/plan-phase/
// detail/elaboration.md` — i.e. relative to the REPO ROOT (`workflowsDir`'s
// grandparent: `workflowsDir` = `<root>/gsd-core/workflows`), not relative
// grandparent: `workflowsDir` = `<root>/msd-core/workflows`), not relative
// to `workflowsDir` itself (verified against plan-phase.md's real
// references; resolving against `workflowsDir` directly double-prefixes
// `gsd-core/workflows/` and false-positives every real reference).
// `msd-core/workflows/` and false-positives every real reference).
const repoRoot = path.join(workflowsDir, '..', '..');
const referenced = new Set(spineContent.match(DETAIL_REFERENCE_PATTERN) || []);
for (const referencedPath of referenced) {

View File

@@ -2,7 +2,7 @@
/**
* Shared library for the compact-content VARIANT guard (ADR-4139, epic #4139,
* Phase 6 #4406). See `gsd-core/references/compact-content-gate.md` §"Streams
* Phase 6 #4406). See `msd-core/references/compact-content-gate.md` §"Streams
* 1b and 4 — variant resolution" for the operational rule this module checks;
* this file is the mechanics, not the source of truth for behavior.
*
@@ -22,7 +22,7 @@
* compact path is never named by any spine's "Read ... variant
* resolution" call site is unwired dead weight).
* 3. Protected content preserved — `checkProtectedContentPreserved` (a
* `<!-- gsd:protected -->` block's lines must appear verbatim in BOTH
* `<!-- msd:protected -->` block's lines must appear verbatim in BOTH
* files, since nothing is "moved" in a variant pair — it is duplicated).
* 4. Size smaller — `checkSizeSmaller`.
* 5. Template consumer parity — NOT implemented here; it needs a real
@@ -50,13 +50,13 @@ const { extractProtectedBlocks, normalizeNonTrivialLines } = require('./compact-
/** Default scan roots: everywhere a `.compact.md` sibling can legally live. */
const DEFAULT_VARIANT_ROOTS = [
path.join(__dirname, '..', '..', 'gsd-core', 'workflows'),
path.join(__dirname, '..', '..', 'gsd-core', 'templates'),
path.join(__dirname, '..', '..', 'msd-core', 'workflows'),
path.join(__dirname, '..', '..', 'msd-core', 'templates'),
];
/** Every markdown-source root a spine/fragment might name a variant path from. */
const DEFAULT_SEARCH_ROOTS = [
path.join(__dirname, '..', '..', 'gsd-core', 'workflows'),
path.join(__dirname, '..', '..', 'msd-core', 'workflows'),
];
/**
@@ -158,16 +158,16 @@ function checkRegistration(pairs) {
* at least one markdown file under `searchRoots` (a spine's "Read ... variant
* resolution" call site). Three needle forms, matched differently, because
* this corpus has two live conventions for naming these paths (found by
* walking up from the compact file itself to its nearest `gsd-core` ancestor,
* walking up from the compact file itself to its nearest `msd-core` ancestor,
* so this works the same way against the real repo and against a fixture
* that builds its own `<tmp>/gsd-core/...` tree):
* that builds its own `<tmp>/msd-core/...` tree):
*
* - The `gsd-core/<rest>` form (e.g. `gsd-core/workflows/autonomous/steps/
* - The `msd-core/<rest>` form (e.g. `msd-core/workflows/autonomous/steps/
* converge-fail-fast.md`'s own convention) is unambiguous on its own — a
* different, longer path coincidentally ending in this exact multi-segment
* suffix is not a realistic false positive, so a plain substring match is
* sufficient without the "unprefixed" guard below.
* - The `<rest>` form without the leading `gsd-core/` (e.g. `workflows/help/
* - The `<rest>` form without the leading `msd-core/` (e.g. `workflows/help/
* modes/full.compact.md`, `help.md`'s own dispatch-table convention) is
* equally unambiguous for the same reason.
* - The bare `<stem>.compact.md` form has no such guarantee — a same-named
@@ -192,11 +192,11 @@ function checkReachability(pairs, searchRoots = DEFAULT_SEARCH_ROOTS) {
}
for (const pair of pairs) {
if (!pair.canonicalExists) continue; // already reported by checkRegistration
const gsdCoreRelative = relativeToNearestGsdCore(pair.compactPath);
const msdCoreRelative = relativeToNearestMsdCore(pair.compactPath);
const stem = path.basename(pair.compactPath, COMPACT_SUFFIX);
const bareNeedle = `${stem}${COMPACT_SUFFIX}`;
const reached = haystacks.some((text) => {
if (gsdCoreRelative && (text.includes(`gsd-core/${gsdCoreRelative}`) || text.includes(gsdCoreRelative))) {
if (msdCoreRelative && (text.includes(`msd-core/${msdCoreRelative}`) || text.includes(msdCoreRelative))) {
return true;
}
return isUnprefixedMatch(text, bareNeedle);
@@ -210,17 +210,17 @@ function checkReachability(pairs, searchRoots = DEFAULT_SEARCH_ROOTS) {
/**
* Walk up from `filePath` to the nearest ancestor directory literally named
* `gsd-core`, and return the path from there to `filePath` (POSIX-separated).
* `msd-core`, and return the path from there to `filePath` (POSIX-separated).
* Returns `null` if no such ancestor exists. Anchoring on the literal
* `gsd-core` segment — rather than a hardcoded repo-root constant — is what
* `msd-core` segment — rather than a hardcoded repo-root constant — is what
* lets this match both the real repo and a fixture built under its own
* `<tmp>/gsd-core/...` tree the same way.
* `<tmp>/msd-core/...` tree the same way.
* @param {string} filePath
* @returns {string | null}
*/
function relativeToNearestGsdCore(filePath) {
function relativeToNearestMsdCore(filePath) {
const segments = filePath.split(path.sep);
const idx = segments.lastIndexOf('gsd-core');
const idx = segments.lastIndexOf('msd-core');
if (idx === -1) return null;
return segments.slice(idx + 1).join('/');
}
@@ -244,7 +244,7 @@ function findMarkdownFiles(dir) {
* lines in the canonical file must also appear (verbatim, after the same
* normalization the partition guard uses) somewhere in the compact sibling.
* Unlike the partition guard, this is NOT a sentinel-presence check on the
* compact file itself — the compact file need not carry `<!-- gsd:protected -->`
* compact file itself — the compact file need not carry `<!-- msd:protected -->`
* markers of its own, since it is not itself audited for content it might
* shed later; it only must not have DROPPED the protected wording.
*

View File

@@ -2,7 +2,7 @@
/**
* Preload fixture for #3808 — pin the SPAWNED context-monitor hook's
* `Date.now()` to `GSD_TEST_NOW_MS`, so any wall-clock boundary can be driven
* `Date.now()` to `MSD_TEST_NOW_MS`, so any wall-clock boundary can be driven
* at exactly limit-1 / limit / limit+1. Added for STALE_SECONDS (review round 3,
* Major 2); now also drives AC2, AC3, the COMPACT_GRACE_SECONDS trio and the
* WATERMARK_SKEW_SECONDS trio, which is what lets those rows use the real
@@ -17,7 +17,7 @@
* render a date string inside the breadcrumb text, never for comparisons.
*/
const fixed = Number(process.env.GSD_TEST_NOW_MS || '');
const fixed = Number(process.env.MSD_TEST_NOW_MS || '');
if (Number.isFinite(fixed) && fixed > 0) {
Date.now = () => fixed;

View File

@@ -2,15 +2,15 @@
/**
* Preload fixture for #3709 review Blocker 2 — force `fs.unlinkSync` to throw
* EPERM inside the SPAWNED gsd-context-monitor.js hook, so the PreCompact
* EPERM inside the SPAWNED msd-context-monitor.js hook, so the PreCompact
* unlink-failure fallback branch is actually executed.
*
* `GSD_TEST_UNLINK_EPERM_MATCH` selects WHICH unlink fails: any path containing
* `MSD_TEST_UNLINK_EPERM_MATCH` selects WHICH unlink fails: any path containing
* the substring throws EPERM; every other path unlinks for real. That lets one
* row fail only the warn sentinel (`-warned.json`) and another only the metrics
* bridge, so each half of the fallback is pinned separately.
*
* Loaded via `node --require <this file> hooks/gsd-context-monitor.js` — the
* Loaded via `node --require <this file> hooks/msd-context-monitor.js` — the
* same seam as `shadow-report-throws-preload.cjs`, and for the same reasons:
* this is NOT a chmod/mode-bit trick (CONTRIBUTING.md: those no-op under root,
* so Docker/CI would pass the test with zero coverage), and NOT the in-process
@@ -21,7 +21,7 @@
const fs = require('fs');
const realUnlinkSync = fs.unlinkSync;
const match = process.env.GSD_TEST_UNLINK_EPERM_MATCH || '';
const match = process.env.MSD_TEST_UNLINK_EPERM_MATCH || '';
if (match) {
fs.unlinkSync = function unlinkSyncWithInjectedEperm(p) {
@@ -34,12 +34,12 @@ if (match) {
};
}
// `GSD_TEST_LSTAT_CLAIMS_FILE_MATCH`: make lstat CLAIM a regular file for
// `MSD_TEST_LSTAT_CLAIMS_FILE_MATCH`: make lstat CLAIM a regular file for
// matching paths — the lstat→open substitution-race shape (a symlink swapped in
// after the lstat), so the O_NOFOLLOW backstop is the guard actually under test
// (review of #3808, round 3, Minor 3). The real stat object is returned with
// only isFile overridden; everything else stays truthful.
const lstatMatch = process.env.GSD_TEST_LSTAT_CLAIMS_FILE_MATCH || '';
const lstatMatch = process.env.MSD_TEST_LSTAT_CLAIMS_FILE_MATCH || '';
if (lstatMatch) {
const realLstatSync = fs.lstatSync;
@@ -53,14 +53,14 @@ if (lstatMatch) {
// being the guard under test (Codex review of #3808, round 3). The row
// asserts this file exists.
try {
fs.writeFileSync(`${p}.gsd-test-lstat-claimed`, '1');
fs.writeFileSync(`${p}.msd-test-lstat-claimed`, '1');
} catch { /* marker is best-effort; the row will fail loudly without it */ }
}
return st;
};
}
// `GSD_TEST_SHRINK_AFTER_LSTAT_MATCH`: TRUNCATE a matching file immediately after
// `MSD_TEST_SHRINK_AFTER_LSTAT_MATCH`: TRUNCATE a matching file immediately after
// `lstatSync` has measured it, so the subsequent `readSync` returns FEWER bytes than
// the size the guard allocated for. That is the shape round 11's Minor names — a
// concurrent legitimate writer truncating mid-write, independent of the planted-object
@@ -69,7 +69,7 @@ if (lstatMatch) {
//
// Layered over the lstat wrapper above rather than folded into it: the two injections
// are selected by different env vars and a row may want either alone.
const shrinkMatch = process.env.GSD_TEST_SHRINK_AFTER_LSTAT_MATCH || '';
const shrinkMatch = process.env.MSD_TEST_SHRINK_AFTER_LSTAT_MATCH || '';
if (shrinkMatch) {
const beforeShrink = fs.lstatSync;
@@ -85,19 +85,19 @@ if (shrinkMatch) {
// landed or not. The marker is the only durable evidence that this injection fired.
try {
fs.truncateSync(p, 1);
fs.writeFileSync(`${p}.gsd-test-shrunk`, '1');
fs.writeFileSync(`${p}.msd-test-shrunk`, '1');
} catch { /* best effort — the row asserts the marker and fails loudly without it */ }
}
return st;
};
}
// `GSD_TEST_SHORT_WRITE_MATCH`: make `fs.writeSync` write only the FIRST BYTE for a
// `MSD_TEST_SHORT_WRITE_MATCH`: make `fs.writeSync` write only the FIRST BYTE for a
// matching fd's payload, once. A short write is permitted by the syscall and was
// previously discarded, so a truncated sentinel reached disk and every later read
// rejected it — the debounce accounting and the compaction watermark silently lost.
// The injection fires once so the retry loop under test can complete normally.
const shortWriteMatch = process.env.GSD_TEST_SHORT_WRITE_MATCH || '';
const shortWriteMatch = process.env.MSD_TEST_SHORT_WRITE_MATCH || '';
if (shortWriteMatch) {
const realWriteSync = fs.writeSync;
@@ -105,7 +105,7 @@ if (shortWriteMatch) {
fs.writeSync = function writeSyncShort(fd, buf, off, len) {
if (!fired && Buffer.isBuffer(buf) && len > 1) {
fired = true;
try { fs.writeFileSync(`${shortWriteMatch}.gsd-test-short-write`, '1'); } catch { /* marker best effort */ }
try { fs.writeFileSync(`${shortWriteMatch}.msd-test-short-write`, '1'); } catch { /* marker best effort */ }
return realWriteSync.call(fs, fd, buf, off, 1);
}
return realWriteSync.apply(fs, arguments);

View File

@@ -39,7 +39,7 @@ const RELATIVE_SPEC_RE = /^\.{1,2}[\\/]/;
* script relies on exactly this (`scripts/check-coverage-gate.cjs` has a
* top-level `return`), so dropping the flag silently breaks the #2858
* packaging guard that scans it. Do not remove this without re-verifying
* every shipped `scripts/**`, `bin/**`, and `gsd-core/bin/**` .cjs/.js file
* every shipped `scripts/**`, `bin/**`, and `msd-core/bin/**` .cjs/.js file
* still parses.
*/
const PARSE_ATTEMPTS = [
@@ -160,7 +160,7 @@ function escapesContainment(rel) {
*
* Throws when a relative require does not resolve on disk, or when the entry
* or a dependency resolves outside the repo. Unresolved-require failures are
* nearly always an unbuilt artifact (`gsd-core/bin/lib/*.cjs` requires
* nearly always an unbuilt artifact (`msd-core/bin/lib/*.cjs` requires
* `npm run build:lib`), and failing here names the cause instead of letting
* the spawned subprocess die with a bare MODULE_NOT_FOUND.
*
@@ -255,7 +255,7 @@ function copyScriptWithDeps(repoRoot, fixtureRoot, scriptRel) {
`copyScriptWithDeps(${entry}) could not resolve ${unresolved.length} ` +
`relative require(s); the fixture would fail with MODULE_NOT_FOUND:\n` +
unresolved.map((u) => ` - ${u}`).join('\n') +
`\nIf these are compiled artifacts under gsd-core/bin/lib/, run \`npm run build:lib\`.`,
`\nIf these are compiled artifacts under msd-core/bin/lib/, run \`npm run build:lib\`.`,
);
}

View File

@@ -42,10 +42,10 @@
*
* Keep it that way: anything that sets this variable is asserting "use THIS, or stop".
*/
const BASELINE_ENV = 'GSD_EMITTED_BASELINE';
const BASELINE_ENV = 'MSD_EMITTED_BASELINE';
/** Default on-disk cache location, relative to the repo root. */
const DEFAULT_CACHE_PATH = '.gsd-cache/emitted-baseline.json';
const DEFAULT_CACHE_PATH = '.msd-cache/emitted-baseline.json';
/** Baseline artifact schema version — pinned so a format change fails loudly.
* v2 (#3547): the harness now measures every global family in the REAL
@@ -115,7 +115,7 @@ function validateBaseline(doc, expectedSha, source) {
* Resolve the baseline through the documented precedence, reporting WHICH step supplied
* it so a failure message can say where the answer came from.
*
* 1. `GSD_EMITTED_BASELINE` — an operator pin; a mismatch here is a HARD STOP
* 1. `MSD_EMITTED_BASELINE` — an operator pin; a mismatch here is a HARD STOP
* 2. the on-disk cache, validated against the expected sha — a mismatch RECOVERS
* 3. an in-job build at `origin/next` (slow fallback)
* 4. none → explicit failure (NEVER a silent pass)

View File

@@ -150,11 +150,11 @@ const REMEDIATION = Object.freeze({
addTrailerGrowth:
'Add a trailer to a commit in this PR (never a new file). Use the '
+ `\`${ACK_TRAILER_GROWTH}\` trailer for growth (key = the bare filename as it `
+ 'appears under gsd-core/workflows/ or agents/).',
+ 'appears under msd-core/workflows/ or agents/).',
doNotRegenerate:
'Do NOT regenerate anything to silence this — there is nothing left to regenerate.',
/** The size ratchet keys on `entry.name` from readdirSync (emitted-runtime.cjs `currentSizes`). */
growthKeyRule: 'Key on the BARE FILENAME as it appears under gsd-core/workflows/ or agents/',
growthKeyRule: 'Key on the BARE FILENAME as it appears under msd-core/workflows/ or agents/',
/** The hash pass keys on the emitted manifest path, which always carries a `/`. */
rippleKeyRule: 'Key on the EMITTED PATH exactly as printed above',
rippleReason: '<why this ripple is deliberate>',
@@ -171,7 +171,7 @@ const REMEDIATION = Object.freeze({
* negative-space note.
*/
ackTrailerExample: renderAckTrailer(
ACK_TRAILER_HASH, 'skills/gsd-add-tests/SKILL.md', 'converter rewrite, ADR-2719',
ACK_TRAILER_HASH, 'skills/msd-add-tests/SKILL.md', 'converter rewrite, ADR-2719',
),
});

View File

@@ -30,9 +30,9 @@
* stays hard-coded, so a dest change still fails loud.
*
* ── The trap this table exists to avoid ─────────────────────────────────────
* The repo contains `skills/gsd-<stem>/SKILL.md` (71 dirs) that LOOK like the source
* The repo contains `skills/msd-<stem>/SKILL.md` (71 dirs) that LOOK like the source
* of the emitted `skills/` family. They are not: scripts/gen-plugin-skills.cjs
* GENERATES them from commands/gsd/*.md, and the installer stages from commands/gsd/
* GENERATES them from commands/msd/*.md, and the installer stages from commands/msd/
* directly (src/install-profiles.cts:637-708). Attributing emitted skills to repo
* skills/ would be false attribution that still passes totality — the exact residual
* risk ADR-2719 records. The spot-check tests pin this pair.
@@ -59,14 +59,14 @@ const { MANIFEST_FAMILIES, runMinimalInstall, buildParityManifest } = require('.
const EXPECTED_MANIFEST_COUNT = MANIFEST_FAMILIES.length;
// ─── Emitted roots ────────────────────────────────────────────────────────────
// Longest-first: `skills/gsd` (hermes category dir) must win over `skills` for
// `skills/gsd/...`, and `.agents/skills` must win over `.agents`.
// Longest-first: `skills/msd` (hermes category dir) must win over `skills` for
// `skills/msd/...`, and `.agents/skills` must win over `.agents`.
const SKILLS_ROOTS = ['skills/gsd', '.agents/skills', 'skills'];
const SKILLS_ROOTS = ['skills/msd', '.agents/skills', 'skills'];
const HOOKS_ROOTS = ['.kimi-code/hooks', '.kimi/hooks', 'hooks'];
/** Source-of-truth command dir every skill/command surface converts from. */
const COMMANDS_SRC = 'commands/gsd';
const COMMANDS_SRC = 'commands/msd';
/** Installer source file that emits the Cline/AGENTS.md instruction bodies as
* code literals (buildClineRulesBody / buildClineAgentsMdBody /
@@ -168,7 +168,7 @@ const AGENT_TRANSFORM_SRCS = [
// #3738: antigravity's global skills pass through the antigravity converter
// (src/runtime-artifact-conversion.cts, mirrored hand-authored in bin/install.js
// per ADR-1508), whose rewrites — e.g. ~/.claude/skills/ → ~/.gemini/config/skills/
// — can move emitted bytes with NO commands/gsd source changing. Declaring the
// — can move emitted bytes with NO commands/msd source changing. Declaring the
// transform here attributes that ripple class permanently, the same way
// AGENT_TRANSFORM_SRCS does for agents; scoped to runtime 'antigravity' so a
// converter change never blankets the other skills runtimes' attribution.
@@ -179,7 +179,7 @@ const ANTIGRAVITY_SKILL_TRANSFORM_SRCS = [
// #4002: zcode's command AND skill bodies flow through `_applyRuntimeRewrites`
// (converter: null — the rewrite pass is their only path-rewriting step), so a
// converter change moves emitted bytes with no commands/gsd source changing.
// converter change moves emitted bytes with no commands/msd source changing.
// Same permanent-attribution shape as ANTIGRAVITY_SKILL_TRANSFORM_SRCS (#3738),
// scoped to runtime 'zcode' for the same reason.
const ZCODE_BODY_TRANSFORM_SRCS = [
@@ -214,7 +214,7 @@ const SOURCE_PREFIX_SUFFIX = '/';
* stem (src/install-profiles.cts:696 joins `stem`, not `prefix + stem`).
*/
function stripSkillPrefix(dirName) {
return dirName.startsWith('gsd-') ? dirName.slice(4) : dirName;
return dirName.startsWith('msd-') ? dirName.slice(4) : dirName;
}
/**
@@ -227,10 +227,10 @@ function nativePluginDescriptor(runtime) {
// rather than at module load for callers that never touch this family.
let registry;
try {
registry = require('../../gsd-core/bin/lib/capability-registry.cjs');
registry = require('../../msd-core/bin/lib/capability-registry.cjs');
} catch (err) {
throw new Error(
'emitted-provenance: cannot load gsd-core/bin/lib/capability-registry.cjs ' +
'emitted-provenance: cannot load msd-core/bin/lib/capability-registry.cjs ' +
`(run \`npm run build\` first): ${err.message}`,
);
}
@@ -282,18 +282,18 @@ function nativePluginDescriptor(runtime) {
const PROVENANCE_RULES = [
// ── Verbatim engine payload ────────────────────────────────────────────────
{
id: 'gsd-core-verbatim',
id: 'msd-core-verbatim',
// Markdown payloads pass through copyWithPathReplacement's audience
// filter for non-Copilot runtimes. Non-Markdown payloads remain byte-for-
// byte copies, but one rule has one kind; the match-specific transform
// list below keeps the causal attribution precise.
kind: 'derived',
roots: ['gsd-core'],
// Enumerated subdirs, NOT `.+`: a new gsd-core/<subdir> must fail totality
roots: ['msd-core'],
// Enumerated subdirs, NOT `.+`: a new msd-core/<subdir> must fail totality
// loudly rather than being absorbed silently. Also keeps this mutually
// exclusive with the two synthesized gsd-core top-level files below.
// exclusive with the two synthesized msd-core top-level files below.
pattern: /^(workflows|references|templates|contexts|bin)\/.+$/,
sources: (m) => [`gsd-core/${m[0]}`],
sources: (m) => [`msd-core/${m[0]}`],
transforms: (m, ctx) => (
m[0].endsWith('.md') && RUNTIME_NOTE_FILTERED_FAMILIES.has(ctx.runtime)
? RUNTIME_NOTE_FILTER_TRANSFORM_SRCS
@@ -301,17 +301,17 @@ const PROVENANCE_RULES = [
),
},
{
id: 'gsd-core-commands-corpus',
id: 'msd-core-commands-corpus',
kind: 'rewrite',
roots: ['gsd-core'],
pattern: /^commands\/gsd\/(.+)$/,
sources: (m) => [`commands/gsd/${m[1]}`],
roots: ['msd-core'],
pattern: /^commands\/msd\/(.+)$/,
sources: (m) => [`commands/msd/${m[1]}`],
transforms: [INSTALL_ENGINE_SRC, INSTALLER_SRC],
},
{
id: 'gsd-core-agents-corpus',
id: 'msd-core-agents-corpus',
kind: 'rewrite',
roots: ['gsd-core'],
roots: ['msd-core'],
pattern: /^agents\/(.+)$/,
sources: (m) => [`agents/${m[1]}`],
transforms: [INSTALL_ENGINE_SRC, INSTALLER_SRC],
@@ -330,7 +330,7 @@ const PROVENANCE_RULES = [
id: 'agents-verbatim',
// #2757 (was `identity`): measured against origin/next's own committed fixtures,
// the SAME emitted agents/<name>.md hashes DIFFERENTLY per runtime (e.g. codex vs
// claude for gsd-nyquist-auditor.md) — impossible for a true verbatim copy.
// claude for msd-nyquist-auditor.md) — impossible for a true verbatim copy.
// Verified empirically by installing every runtime and diffing the output against
// the raw repo source: no runtime reproduces it byte-for-byte. Every one rewrites
// frontmatter quoting and the hardcoded `.claude/` self-reference
@@ -340,15 +340,15 @@ const PROVENANCE_RULES = [
// per-runtime `kind`) and why this wholesale reclassification was chosen instead.
kind: 'derived',
roots: ['agents'],
// Excludes `gsd.md`: that is Kimi's ROOT agent, built from a code literal and
// Excludes `msd.md`: that is Kimi's ROOT agent, built from a code literal and
// NOT a repo agent file. Without the exclusion it matched here and resolved to
// `agents/gsd.md`, which does not exist — a false attribution that still passed
// `agents/msd.md`, which does not exist — a false attribution that still passed
// totality, i.e. the exact residual ADR-2719 records. Every repo agent is
// `gsd-<name>.md`, so excluding the bare `gsd.md` is precise.
// `msd-<name>.md`, so excluding the bare `msd.md` is precise.
// `.agent.md` is likewise excluded — Copilot emits a RENAMED copy
// (`<name>.agent.md`) whose source is `agents/<name>.md`; matching it here
// resolved to a file that does not exist. Same false-attribution class.
pattern: /^(?!gsd\.md$)(?!.*\.agent\.md$)[^/]+\.md$/,
pattern: /^(?!msd\.md$)(?!.*\.agent\.md$)[^/]+\.md$/,
sources: (m) => [`agents/${m[0]}`],
transforms: AGENT_TRANSFORM_SRCS,
},
@@ -401,7 +401,7 @@ const PROVENANCE_RULES = [
// Attribute to the REPO source a PR actually edits, not the build artifact.
// Excludes Copilot's hook-registration JSON (next rule) — that is a code
// literal, not a built script, and attributing it here resolved to a
// nonexistent `hooks/gsd-session.json`. `package.json` is excluded for the
// nonexistent `hooks/msd-session.json`. `package.json` is excluded for the
// same reason (the #2544 `commonjs-marker` rule below): there is no
// `hooks/package.json` in the repo to attribute to.
//
@@ -414,11 +414,11 @@ const PROVENANCE_RULES = [
// (HOOKS_WINDOWS_SHIM_SRC, src/runtime-hooks-surface.cts). The `.cmd` bytes
// are `@ECHO OFF\r\n@SETLOCAL\r\n@<runner> <script> %*\r\n` — built from the
// install-time interpreter token and the absolute install path, plus a
// hardcoded literal filename (e.g. `gsd-check-update.js`) baked into that
// hardcoded literal filename (e.g. `msd-check-update.js`) baked into that
// same source file. The wrapped `.js` file's NAME flows into the `.cmd`
// bytes; its CONTENT never does — see the `sources` comment below for why
// that rules out attributing to `hooks/<name>.js`.
pattern: /^(?!gsd-session\.json$|package\.json$).+$/,
pattern: /^(?!msd-session\.json$|package\.json$).+$/,
// `package.json` (the CommonJS marker) is excluded here and owned by the
// dedicated `commonjs-marker` rule below. #2717 attributed it inside THIS
// rule, routing it to HOOKS_WINDOWS_SHIM_SRC because at that point
@@ -447,11 +447,11 @@ const PROVENANCE_RULES = [
{
id: 'commonjs-marker',
kind: 'code-derived',
// Every root GSD stages its own `.js` files into and therefore pins to
// Every root MSD stages its own `.js` files into and therefore pins to
// CommonJS: the hooks roots, plus the native plugin/extension dirs.
roots: [...HOOKS_ROOTS, 'plugins', 'extensions'],
// #2544: a `{"type":"commonjs"}` module-type marker, written as a code
// literal so Node's ancestor walk resolves GSD's staged `.js` files as
// literal so Node's ancestor walk resolves MSD's staged `.js` files as
// CommonJS under an ambient `"type": "module"`. Like the Copilot
// registration JSON above it is emitted, never built — there is no
// `hooks/package.json` or `plugins/package.json` in the repo, so the
@@ -497,12 +497,12 @@ const PROVENANCE_RULES = [
},
{
// #3023: pi renames the shared hooks bundle's staged directory from the
// default `hooks/` to `gsd-hooks/` (hostBehaviors.sharedHooksDirName,
// default `hooks/` to `msd-hooks/` (hostBehaviors.sharedHooksDirName,
// bin/install.js's resolveSharedHooksDirName). Same family as `hooks-built`
// above (built from hooks/ via scripts/build-hooks.js), staged under a
// different root for exactly one runtime — a dedicated, `runtimes`-scoped
// rule keeps that pi-only rename from ever being able to shadow another
// host's `(rel, runtime)` pair, rather than folding 'gsd-hooks' into the
// host's `(rel, runtime)` pair, rather than folding 'msd-hooks' into the
// shared HOOKS_ROOTS list `hooks-built`/`commonjs-marker` both key off.
// `package.json` (the CommonJS marker) is excluded here and owned by the
// dedicated `pi-shared-hooks-commonjs-marker` rule below, mirroring how
@@ -510,19 +510,19 @@ const PROVENANCE_RULES = [
id: 'pi-shared-hooks-built',
kind: 'derived',
runtimes: new Set(['pi']),
roots: ['gsd-hooks'],
roots: ['msd-hooks'],
pattern: /^(?!package\.json$).+$/,
sources: (m) => [`hooks/${m[0]}`],
},
{
// Companion to `pi-shared-hooks-built`: the CommonJS-mode marker written
// into pi's renamed `gsd-hooks/` root by the same installSharedHooksBundle
// into pi's renamed `msd-hooks/` root by the same installSharedHooksBundle
// call the generic `commonjs-marker` rule attributes for the `hooks/`
// family. Kept as its own pi-scoped rule for the same reason as above.
id: 'pi-shared-hooks-commonjs-marker',
kind: 'code-derived',
runtimes: new Set(['pi']),
roots: ['gsd-hooks'],
roots: ['msd-hooks'],
pattern: /^package\.json$/,
sources: () => [COMMONJS_MARKER_SRC, INSTALLER_SRC],
},
@@ -534,11 +534,11 @@ const PROVENANCE_RULES = [
// by HOOK_CONFIG_FILES because they embed a platform-varying node-runner
// command), this one is platform-stable and stays in the manifest
// (src/runtime-hooks-surface.cts:73,89).
pattern: /^gsd-session\.json$/,
pattern: /^msd-session\.json$/,
sources: () => [CLINE_BODY_SRC],
},
// ── Skill / command surfaces — all convert from commands/gsd/*.md ──────────
// ── Skill / command surfaces — all convert from commands/msd/*.md ──────────
{
id: 'skills-from-commands',
kind: 'rewrite',
@@ -577,7 +577,7 @@ const PROVENANCE_RULES = [
id: 'flat-commands-from-commands',
kind: 'rewrite',
roots: ['commands', 'command'],
pattern: /^gsd-([^/]+)\.md$/,
pattern: /^msd-([^/]+)\.md$/,
sources: (m) => [`${COMMANDS_SRC}/${m[1]}.md`],
// #4482: OpenCode command bodies pass through its command converter.
// #4002: zcode command bodies pass through _applyRuntimeRewrites with
@@ -596,7 +596,7 @@ const PROVENANCE_RULES = [
roots: ['plugins', 'extensions'],
pattern: /^[^/]+\.(js|cjs|mjs)$/,
// Source is per-runtime (opencode -> .opencode/…, kilo -> .kilo/…,
// pi -> pi/gsd.cjs), so attribution is a function of (rel, runtime).
// pi -> pi/msd.cjs), so attribution is a function of (rel, runtime).
sources: (m, ctx) => {
const np = nativePluginDescriptor(ctx.runtime);
if (!np || !np.source) {
@@ -616,15 +616,15 @@ const PROVENANCE_RULES = [
id: 'cline-rules-code-derived',
kind: 'code-derived',
roots: ['.clinerules'],
pattern: /^(gsd\.md|hooks\/PreToolUse)$/,
pattern: /^(msd\.md|hooks\/PreToolUse)$/,
sources: () => [CLINE_BODY_SRC],
},
// #3547 removed `agents-md-code-derived` (roots: ['.agents'],
// ^AGENTS\.md$) and `synthesized-gsd-defaults` (^\.gsd/defaults\.json$):
// ^AGENTS\.md$) and `synthesized-msd-defaults` (^\.msd/defaults\.json$):
// both paths only appeared in a manifest while the harness collapsed
// configDir onto the sandbox HOME, walking HOME-level siblings
// (cline's ~/.agents/AGENTS.md, every non-Claude runtime's
// ~/.gsd/defaults.json). With the harness installing into each runtime's
// ~/.msd/defaults.json). With the harness installing into each runtime's
// real global subdirectory those files sit outside the walked configDir,
// the rules matched nothing, and the totality guard's dead-rule arm fired
// — exactly as designed. The files are still written, still covered by the
@@ -633,7 +633,7 @@ const PROVENANCE_RULES = [
{
id: 'hermes-category-description',
kind: 'code-derived',
roots: ['skills/gsd'],
roots: ['skills/msd'],
pattern: /^DESCRIPTION\.md$/,
sources: () => [INSTALLER_SRC],
},
@@ -645,7 +645,7 @@ const PROVENANCE_RULES = [
// (src/runtime-artifact-layout.cts:303), and the YAML additionally enumerates
// every staged subagent — so adding or removing an agents/*.md legitimately
// moves this file too. Both sources are declared.
pattern: /^gsd\.(yaml|md)$/,
pattern: /^msd\.(yaml|md)$/,
sources: () => [KIMI_ROOT_AGENT_SRC, 'agents/'],
},
@@ -658,7 +658,7 @@ const PROVENANCE_RULES = [
// `{"type":"commonjs"}` CommonJS-mode marker as the root one, written into
// each Kimi product's separate hooks root (installSharedHooksBundle; the
// per-runtime root split is #2755).
pattern: /^(\.gsd-profile|package\.json|\.kimi(-code)?\/package\.json|gsd-core\/VERSION|gsd-core\/\.gsd-runtime)$/,
pattern: /^(\.msd-profile|package\.json|\.kimi(-code)?\/package\.json|msd-core\/VERSION|msd-core\/\.msd-runtime)$/,
sources: () => [],
},
{
@@ -675,8 +675,8 @@ const PROVENANCE_RULES = [
/**
* Reject an emitted key that could escape the repo once turned into a source path.
*
* Two rules (`gsd-core-verbatim`, `scripts-verbatim`) capture a whole tail with
* `.+` rather than `[^/]+`, so a key like `gsd-core/workflows/../../../etc/passwd`
* Two rules (`msd-core-verbatim`, `scripts-verbatim`) capture a whole tail with
* `.+` rather than `[^/]+`, so a key like `msd-core/workflows/../../../etc/passwd`
* would otherwise produce a source path that `path.join(REPO_ROOT, src)` resolves
* OUTSIDE the repo. Nothing reachable today exploits it — real manifest keys come
* from installer output and the only consumer is an `fs.existsSync` probe — but

View File

@@ -47,7 +47,7 @@ const {
} = require('./install-shared.cjs');
const { ACK_TRAILER_HASH, ACK_TRAILER_GROWTH, parseAckTrailers } = require('./emitted-diff.cjs');
const { runGit, OUTCOME } = require('./process-seam.cjs');
const { escapeRegex } = require('../../gsd-core/bin/lib/pattern.cjs');
const { escapeRegex } = require('../../msd-core/bin/lib/pattern.cjs');
const REPO_ROOT = path.join(__dirname, '..', '..');
const FIXTURE_SUBDIR = 'tests/fixtures/golden-install-parity';
@@ -141,7 +141,7 @@ function touchesRuntimeRegistry(changedPaths) {
* that shrank uniformly, which a same-count self-check passes vacuously.
*
* Pure and IO-free by construction: the real-tree caller skips wherever no base ref
* exists (the gsd-test runner shallow-clones, so `origin/*` is absent), which would make
* exists (the msd-test runner shallow-clones, so `origin/*` is absent), which would make
* a regression written at that altitude silently skip instead of proving anything.
*
* @param {object} o
@@ -299,19 +299,19 @@ function resolveBaseSha(base = 'origin/next') {
* Base-ref candidates, most-specific first.
*
* The differential needs a ref for `next`, and that ref is NOT universally present:
* - the gsd-test runner shallow-clones and merges base+head, so no `origin/*`
* - the msd-test runner shallow-clones and merges base+head, so no `origin/*`
* remote-tracking refs exist in the container (verified: `git rev-parse
* origin/next` fails there, which is what turned this test red on its first run);
* - GitHub Actions' checkout does not create remote-tracking branches for OTHER
* branches by default, which is exactly why `changeset-required.yml` carries an
* explicit `git fetch origin "${BASE_REF}:refs/remotes/origin/${BASE_REF}"` step.
*
* `GSD_EMITTED_BASE` lets a lane name the ref (or sha) directly. `GITHUB_BASE_REF` is
* `MSD_EMITTED_BASE` lets a lane name the ref (or sha) directly. `GITHUB_BASE_REF` is
* set by Actions on pull_request events.
*/
function baseRefCandidates(env = process.env) {
const candidates = [];
if (env.GSD_EMITTED_BASE) candidates.push(env.GSD_EMITTED_BASE);
if (env.MSD_EMITTED_BASE) candidates.push(env.MSD_EMITTED_BASE);
if (env.GITHUB_BASE_REF) {
candidates.push(`origin/${env.GITHUB_BASE_REF}`, env.GITHUB_BASE_REF);
}
@@ -325,7 +325,7 @@ function baseRefCandidates(env = process.env) {
* Returning null is NOT a pass — the caller turns it into an explicit `t.skip()` with
* the full candidate list in the message, so an environment where the gate did not run
* says so out loud. A bare `return` there would be a PASS (ADR-2719 §6), and a hard
* failure would make the suite permanently red in the gsd-test container, where no
* failure would make the suite permanently red in the msd-test container, where no
* base ref can exist by construction.
*/
function resolveBase(env = process.env) {
@@ -431,7 +431,7 @@ function baselineManifestsAtRef(base = 'origin/next') {
* needs no `npm ci`: `bin/install.js` and the `tests/helpers/*.cjs` real-tree shells are
* all Node-builtins-only (CONTRIBUTING.md's "No external dependencies in core"). It DOES
* need `npm run build:lib` run there first, though — `tests/helpers/install-shared.cjs`
* requires the TSC-COMPILED `gsd-core/bin/lib/runtime-artifact-layout.cjs`, which is
* requires the TSC-COMPILED `msd-core/bin/lib/runtime-artifact-layout.cjs`, which is
* gitignored, not committed, and therefore absent from a bare worktree checkout, and
* `<worktreeDir>/bin/install.js` (spawned BY `cwd`'s generator via `currentManifests`'s
* `repoRoot` override) needs its own compiled copy alongside it, not `cwd`'s. `node_modules`
@@ -466,12 +466,12 @@ const BUILD_LIB_TIMEOUT_MS = 180_000;
// observation and 20% above the censored one, while leaving 240s of chunk
// headroom for every other file sharing the chunk.
//
// The old 300s sat INSIDE that variance band. Under gsd-test this slow path runs
// The old 300s sat INSIDE that variance band. Under msd-test this slow path runs
// on every verification, because the on-disk baseline cache is restored by
// actions/cache keyed on github.event.pull_request.base.sha — a key that exists
// only inside GitHub Actions. The real remedy is making that cache reachable from
// the remote runner so the in-job build returns to being the rare fallback
// ADR-2719 §5 describes; that is a gsd-test-runner change, not one this repo can
// ADR-2719 §5 describes; that is a msd-test-runner change, not one this repo can
// make.
const BUILD_TIMEOUT_MS = 360_000;
// Mirrors `scripts/run-tests.cjs:973`'s default. Duplicated deliberately and
@@ -481,11 +481,11 @@ const BUILD_TIMEOUT_MS = 360_000;
const CHUNK_TIMEOUT_CEILING_MS = 600_000;
function buildBaselineAtRef(ref, { cwd = REPO_ROOT } = {}) {
const worktreeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-emitted-baseline-wt-'));
const worktreeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-emitted-baseline-wt-'));
// mkdtempSync already created the directory; `git worktree add` requires the
// target to not exist (or be empty) — remove it and let git recreate it.
fs.rmdirSync(worktreeDir);
const outFile = path.join(os.tmpdir(), `gsd-emitted-baseline-out-${crypto.randomBytes(8).toString('hex')}.json`);
const outFile = path.join(os.tmpdir(), `msd-emitted-baseline-out-${crypto.randomBytes(8).toString('hex')}.json`);
// Per-step timings, carried into the thrown error. A bare "spawnSync ETIMEDOUT"
// names neither the step nor its elapsed time, which is exactly the information
@@ -584,7 +584,7 @@ function baselineSizesAtRef(base = 'origin/next') {
/**
* The package version of the tree whose emitted output is about to be measured.
*
* `bin/install.js` bakes `{{GSD_VERSION}}` -> `pkg.version` into every emitted hook,
* `bin/install.js` bakes `{{MSD_VERSION}}` -> `pkg.version` into every emitted hook,
* and `buildParityManifest`'s `pkgVersion` normalization exists to collapse that stamp
* back to '<VERSION>' so a version bump alone never moves a hash. That normalization is
* only correct when `pkgVersion` matches the version of the tree that PRODUCED the
@@ -697,14 +697,14 @@ function currentManifests({ repoRoot } = {}) {
/**
* Current on-disk sizes for the workflow + agent families the ratchet covers.
* @param {object} [opts]
* @param {string} [opts.repoRoot] - Read `<repoRoot>/gsd-core/workflows` and
* @param {string} [opts.repoRoot] - Read `<repoRoot>/msd-core/workflows` and
* `<repoRoot>/agents` instead of this checkout's own (#2767) — same rationale as
* `currentManifests`'s `repoRoot`.
*/
function currentSizes({ repoRoot = REPO_ROOT } = {}) {
const sizes = {};
for (const [dir, filter] of [
[path.join(repoRoot, 'gsd-core', 'workflows'), (f) => f.endsWith('.md')],
[path.join(repoRoot, 'msd-core', 'workflows'), (f) => f.endsWith('.md')],
[path.join(repoRoot, 'agents'), (f) => f.endsWith('.md')],
]) {
if (!fs.existsSync(dir)) continue;
@@ -747,7 +747,7 @@ function ackTrailerGit(args, { cwd = REPO_ROOT, timeoutMs = GIT_TIMEOUT_MS, inpu
// commit unconditionally — silently bury it: git's parser (correctly, by its own
// contract) stops at the first non-conforming line scanning backward, so it never even
// reaches past `---------` to see the real content underneath, let alone further back
// to an earlier bullet. Confirmed directly against a real squash commit (gsd-core
// to an earlier bullet. Confirmed directly against a real squash commit (msd-core
// 78013b3b74): `%(trailers)` there returns ONLY GitHub's own two `Co-authored-by:`
// lines — not even the last original commit's own trailer survives, only GitHub's
// appended one.
@@ -844,7 +844,7 @@ function extractSquashBuriedTrailers(rawBody, { cwd, timeoutMs }) {
// Record/field/value separators for the `git log --format` trailer extraction below.
// ASCII control characters (RS/US/GS) so they can never collide with real trailer
// content, following the precedent at gsd-core/workflows/ship.md:312 (`%x1f`/`%x1e` for
// content, following the precedent at msd-core/workflows/ship.md:312 (`%x1f`/`%x1e` for
// a different `%(trailers:...)` extraction in this same repo).
const ACK_TRAILER_RECORD_SEP = '\x1e'; // between commits
const ACK_TRAILER_FIELD_SEP = '\x1f'; // between the hash-space list and growth-space list

View File

@@ -9,7 +9,7 @@
* Any call that supplies `--out` without also supplying matching overrides
* for the other four would, under `--write`, clobber the real committed
* `scripts/lib/exit-code-registry.cjs`, `hooks/lib/exit-code-registry.js`,
* `src/exit-code-registry.d.cts`, and `gsd-core/bin/shared/exit-codes.sh` —
* `src/exit-code-registry.d.cts`, and `msd-core/bin/shared/exit-codes.sh` —
* dangerous since test files in this repo run in parallel.
*
* `ensureScriptsOut` derives co-located, per-call-unique secondary/hooks/

View File

@@ -9,13 +9,13 @@
*
* Settings:
* numRuns: 200 — enough to catch boundary bugs without slow CI
* seed: 42 — deterministic across CI runs; set GSD_FC_SEED=<n> to
* seed: 42 — deterministic across CI runs; set MSD_FC_SEED=<n> to
* override locally for exploration
*/
const fc = require('fast-check');
const seed = process.env.GSD_FC_SEED ? Number(process.env.GSD_FC_SEED) : 42;
const seed = process.env.MSD_FC_SEED ? Number(process.env.MSD_FC_SEED) : 42;
fc.configureGlobal({
numRuns: 200,

View File

@@ -50,7 +50,7 @@ function commitEmpty(cwd, message) {
// optional .last-build-status.json written as autoUpdateValue.
// autoUpdateValue === null means no status file is written.
function makeStatusProject(autoUpdateValue) {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-3347-status-'));
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-3347-status-'));
gitOrThrow(['init', '-q', '-b', 'main'], { cwd: tmpDir });
gitOrThrow(['config', 'user.email', 'test@example.com'], { cwd: tmpDir });
gitOrThrow(['config', 'user.name', 'Test'], { cwd: tmpDir });

View File

@@ -12,7 +12,7 @@
* exported `HOOKS_TO_COPY` list. This replaces a former `.js`-extension-count
* heuristic ("populated" if at least one `.js` file exists), which could not
* see a dist missing exactly the file class that caused #3108: build-hooks
* also ships `.sh` files (e.g. `gsd-session-state.sh`), so a dist with every
* also ships `.sh` files (e.g. `msd-session-state.sh`), so a dist with every
* `.js` present and every `.sh` absent read as fully populated and the
* rebuild was silently skipped. The set-membership check has no such blind
* spot: any expected entry missing, of any extension, is stale. It does NOT
@@ -50,7 +50,7 @@ const BUILD_HOOKS_SCRIPT = path.join(REPO_ROOT, 'scripts', 'build-hooks.js');
* expects to have copied there (`HOOKS_TO_COPY`, bare top-level filenames,
* and `HOOKS_SUBDIRS_TO_COPY`, bare subdirectory names such as `lib`). A dist
* with every top-level file present but no `lib/` (e.g. missing
* `gsd-graphify-rebuild.sh`) is exactly the same blindness the old
* `msd-graphify-rebuild.sh`) is exactly the same blindness the old
* `.js`-count heuristic had, one level down — so each subdir entry is first
* checked against the same top-level readdir Set, THEN additionally required
* to be a readable, non-empty directory (one extra `readdirSync` per subdir
@@ -85,7 +85,7 @@ function isHooksDistStale(dir) {
// Presence in the top-level Set only proves an entry named `lib`
// exists — not that it is a directory, nor that it is populated. A
// stray regular file named `lib`, or an empty `lib/` missing e.g.
// `gsd-graphify-rebuild.sh`, would otherwise read as fresh: exactly
// `msd-graphify-rebuild.sh`, would otherwise read as fresh: exactly
// the blind spot the subdir check exists to close. One `readdirSync`
// per subdir entry (there is exactly one, `lib`) — no per-expected-file
// `existsSync`/`statSync`, keeping the added cost to one syscall total.

View File

@@ -23,11 +23,11 @@ const { runNode } = require('./process-seam.cjs');
const {
resolveRuntimeArtifactLayout,
} = require('../../gsd-core/bin/lib/runtime-artifact-layout.cjs');
const { escapeRegex: escapeRegExp } = require('../../gsd-core/bin/lib/pattern.cjs');
} = require('../../msd-core/bin/lib/runtime-artifact-layout.cjs');
const { escapeRegex: escapeRegExp } = require('../../msd-core/bin/lib/pattern.cjs');
const INSTALL_SCRIPT = path.join(__dirname, '..', '..', 'bin', 'install.js');
const MANIFEST_NAME = 'gsd-file-manifest.json';
const MANIFEST_NAME = 'msd-file-manifest.json';
// #3145: class-norm timeout (bounds the previously-unbounded spawnSync in
// runMinimalInstall) — not a per-suite value. See helpers/timeouts.cjs for
@@ -39,23 +39,23 @@ const BUILD_SCRIPT = path.join(__dirname, '..', '..', 'scripts', 'build-hooks.js
const HOOKS_DIST = path.join(__dirname, '..', '..', 'hooks', 'dist');
const EXPECTED_SH_HOOKS = [
'gsd-session-state.sh',
'gsd-validate-commit.sh',
'gsd-phase-boundary.sh',
'msd-session-state.sh',
'msd-validate-commit.sh',
'msd-phase-boundary.sh',
];
const EXPECTED_ALL_HOOKS = [
'gsd-agent-isolation-guard.js',
'gsd-check-update.js',
'gsd-config-reload.js',
'gsd-context-monitor.js',
'msd-agent-isolation-guard.js',
'msd-check-update.js',
'msd-config-reload.js',
'msd-context-monitor.js',
// #997: SessionStart canonical-path bootstrap for plugin installs.
'gsd-ensure-canonical-path.js',
'gsd-prompt-guard.js',
'gsd-read-guard.js',
'gsd-read-injection-scanner.js',
'gsd-statusline.js',
'gsd-workflow-guard.js',
'msd-ensure-canonical-path.js',
'msd-prompt-guard.js',
'msd-read-guard.js',
'msd-read-injection-scanner.js',
'msd-statusline.js',
'msd-workflow-guard.js',
...EXPECTED_SH_HOOKS,
];
@@ -87,8 +87,8 @@ const RUNTIME_META = {
*
* NOT simply `Object.keys(RUNTIME_META)`: that has 18 entries while the fixture set has
* 19. The extra one is `claude-local` — claude is the reference host and the ONLY
* runtime with a distinct LOCAL "legacy flat-commands" layout (`commands/gsd-*.md` +
* `agents/gsd-*.md` at project scope), which `golden-install-parity.test.cjs` guards
* runtime with a distinct LOCAL "legacy flat-commands" layout (`commands/msd-*.md` +
* `agents/msd-*.md` at project scope), which `golden-install-parity.test.cjs` guards
* with a hand-coded test outside its RUNTIME_META loop (#2086).
*
* Enumerating from RUNTIME_META alone dropped that family from BOTH sides of the
@@ -136,7 +136,7 @@ const SKILL_RUNTIMES = [
// This checkout's own package version — the DEFAULT normalized in hash computation so
// the golden is stable across version bumps (the rc step runs `npm version X.Y.Z-rc.N`
// before tests, which rebakes the version into hook files and gsd-core/VERSION).
// before tests, which rebakes the version into hook files and msd-core/VERSION).
//
// This is only a default. buildParityManifest's `pkgVersion` option exists precisely
// because the tree being MEASURED is not always this checkout (#2767's `repoRoot`
@@ -147,24 +147,24 @@ const SKILL_RUNTIMES = [
const PKG_VERSION = require('../../package.json').version;
// Volatile metadata files always excluded from the parity manifest.
// .gsd-source (#1477, claude-global only) records the install-time absolute path
// to the package's commands/gsd source tree, which is the checkout/CI workspace
// .msd-source (#1477, claude-global only) records the install-time absolute path
// to the package's commands/msd source tree, which is the checkout/CI workspace
// path — NOT the temp HOME root, so it is never normalized to '<HOME>' and its
// hash varies by environment. Excluded for the same reason as gsd-install-state.json.
// gsd-core/CHANGELOG.md is excluded because it contains historical version strings
// hash varies by environment. Excluded for the same reason as msd-install-state.json.
// msd-core/CHANGELOG.md is excluded because it contains historical version strings
// that cause hash drift between local (PKG_VERSION=1.x.x) and CI (PKG_VERSION=1.x.x-rc.N):
// the PKG_VERSION normalization below replaces only the *current* version, but
// CHANGELOG.md references prior-release versions, so the normalized hash diverges.
// gsd-file-manifest.json's exclusion was revisited deliberately for #2872: the
// msd-file-manifest.json's exclusion was revisited deliberately for #2872: the
// manifest gained `manifestVersion`/`runtime`/`scope` fields, all of which are
// deterministic and would not by themselves force an exclusion, but `timestamp`
// — the original reason this file is volatile — is unchanged by #2872, so the
// exclusion still holds for exactly the same reason it always has.
const VOLATILE_FILES = new Set([
'gsd-file-manifest.json',
'gsd-install-state.json',
'.gsd-source',
'gsd-core/CHANGELOG.md',
'msd-file-manifest.json',
'msd-install-state.json',
'.msd-source',
'msd-core/CHANGELOG.md',
]);
// Hook-registration config files excluded from the parity manifest. These are
@@ -199,7 +199,7 @@ const HOOK_CONFIG_FILES = new Set(['settings.json', 'settings.local.json', 'hook
// compares base-vs-current on the same machine, so the platform-varying
// node-runner command embedded in the TOML never crosses platforms inside a
// gate (that was a golden-content-era hazard, and the goldens are gone).
// Kimi CLI's config.toml (KIMI_SHARE_DIR root ~/.kimi) lives OUTSIDE its GSD
// Kimi CLI's config.toml (KIMI_SHARE_DIR root ~/.kimi) lives OUTSIDE its MSD
// configDir (~/.config/agents) and never enters the walk. The pre-#3547
// relative-path exclusions ('.kimi/config.toml', '.kimi-code/config.toml')
// existed only for the collapsed shape — where the walked root was the HOME
@@ -208,7 +208,7 @@ const HOOK_CONFIG_FILES = new Set(['settings.json', 'settings.local.json', 'hook
// resolves its own root since #2755.
const HOOK_CONFIG_RELATIVE_PATHS = new Set();
// Path prefixes excluded from the parity manifest. `gsd-core/bin/lib/` holds the
// Path prefixes excluded from the parity manifest. `msd-core/bin/lib/` holds the
// tsc-built runtime artifacts (compiled from src/*.cts) that the install COPIES
// verbatim — they are NOT produced by installRuntimeArtifacts (the move's parity
// scope), and their exact bytes depend on the BUILD environment (a clean tsc
@@ -218,7 +218,7 @@ const HOOK_CONFIG_RELATIVE_PATHS = new Set();
// (e.g. milestone.cjs, roadmap.cjs). The .cts sources are type-checked + drift-
// guarded + coverage-gated elsewhere; this harness asserts the CONVERTED artifact
// output (skills/commands/agents) that the engine actually emits.
const EXCLUDED_PREFIXES = ['gsd-core/bin/lib/'];
const EXCLUDED_PREFIXES = ['msd-core/bin/lib/'];
// ─── Helper functions ─────────────────────────────────────────────────────────
@@ -250,22 +250,22 @@ const SEMVER_ISH_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$/;
* (false negative), while an UNCHANGED file that happens to contain a literal equal to
* only the CURRENT version collapses on one side only and reports as spurious drift
* (false positive). Emitted sources really do carry bare semver literals that are NOT
* install-time stamps — e.g. gsd-core/workflows/update.md's `1.4.0`/`1.3.1` examples,
* agents/gsd-project-researcher.md's `1.2.3`, gsd-core/workflows/help/modes/full.md's
* install-time stamps — e.g. msd-core/workflows/update.md's `1.4.0`/`1.3.1` examples,
* agents/msd-project-researcher.md's `1.2.3`, msd-core/workflows/help/modes/full.md's
* `1.0.0` — and those must stay VISIBLE to the parity gate if they ever change.
*
* So instead of replacing the version everywhere, this only normalizes it at the
* specific places `bin/install.js` actually stamps `pkg.version` into emitted content
* as a version FIELD/marker (not prose):
* - `// gsd-hook-version: <ver>` / `# gsd-hook-version: <ver>` — the
* `{{GSD_VERSION}}` substitution done for every emitted hook file.
* - `// msd-hook-version: <ver>` / `# msd-hook-version: <ver>` — the
* `{{MSD_VERSION}}` substitution done for every emitted hook file.
* - `"version": "<ver>"` — JSON manifests (plugin/extension/capability-style)
* embedding the package version as a string field.
* - `version: "<ver>"` / `version: <ver>` — YAML frontmatter version fields (e.g.
* skill frontmatter's `yamlQuote(pkg.version)`, Hermes' category
* `DESCRIPTION.md`).
* - `@opengsd/gsd-core@<ver>` — pinned package-spec references.
* - a file whose ENTIRE trimmed content IS the version (`gsd-core/VERSION`).
* - `@golem15/msd-core@<ver>` — pinned package-spec references.
* - a file whose ENTIRE trimmed content IS the version (`msd-core/VERSION`).
* Each pattern only matches when the version in the content EQUALS the supplied
* `pkgVersion` — this is deliberately narrow, at the cost of needing a new pattern any
* time the installer grows a new stamp site (see the empirical repro-harness check
@@ -281,11 +281,11 @@ function normalizeVersionStamps(content, pkgVersion) {
// from matching as a PREFIX of a longer version-shaped string it is not equal to
// (e.g. pkgVersion '1.9.0' must not match inside '1.9.0-rc.1' or '1.9.0.1').
return content
.replace(new RegExp(`((?:\\/\\/|#)\\s*gsd-hook-version:\\s*)${v}(?![\\w.+-])`, 'g'), '$1<VERSION>')
.replace(new RegExp(`((?:\\/\\/|#)\\s*msd-hook-version:\\s*)${v}(?![\\w.+-])`, 'g'), '$1<VERSION>')
.replace(new RegExp(`("version"\\s*:\\s*")${v}(")`, 'g'), '$1<VERSION>$2')
.replace(new RegExp(`(version:\\s*")${v}(")`, 'g'), '$1<VERSION>$2')
.replace(new RegExp(`(version:\\s*)${v}(?![\\w.+-])`, 'g'), '$1<VERSION>')
.replace(new RegExp(`(@opengsd/gsd-core@)${v}(?![\\w.+-])`, 'g'), '$1<VERSION>')
.replace(new RegExp(`(@golem15/msd-core@)${v}(?![\\w.+-])`, 'g'), '$1<VERSION>')
.replace(new RegExp(`^(\\s*)${v}(\\s*)$`), '$1<VERSION>$2');
}
@@ -582,18 +582,18 @@ function simulateHookCopy(hooksSrc, hooksDest) {
}
/** Build a clean env for spawned installer processes.
* Must strip GSD_TEST_MODE so the child runs the real install, not the no-op guard. */
* Must strip MSD_TEST_MODE so the child runs the real install, not the no-op guard. */
function installerEnv(overrides = {}) {
// #3156: delegate to the ONE canonical raw-installer-spawn env rather than
// carrying a second shape of it. The installer writes GSD's own user store to
// <home>/.gsd/defaults.json through os.homedir() DIRECTLY
// (bin/install.js writeNonClaudeDefaults, #2834), which reads no GSD variable,
// carrying a second shape of it. The installer writes MSD's own user store to
// <home>/.msd/defaults.json through os.homedir() DIRECTLY
// (bin/install.js writeNonClaudeDefaults, #2834), which reads no MSD variable,
// so no config-location scrub can reach it — only a sandboxed HOME can. Every
// caller that already passes an explicit { HOME, USERPROFILE } still wins:
// overrides spread last.
const { installSpawnEnv } = require('../helpers.cjs');
const env = installSpawnEnv(overrides);
delete env.GSD_TEST_MODE;
delete env.MSD_TEST_MODE;
return env;
}
@@ -614,7 +614,7 @@ function installerEnv(overrides = {}) {
*/
function runMinimalInstall({ runtime, scope, extraArgs = [], installScript = INSTALL_SCRIPT, root: providedRoot = null, extraEnv = {} }) {
const ownsRoot = providedRoot === null;
const root = providedRoot ?? fs.mkdtempSync(path.join(os.tmpdir(), `gsd-${runtime}-${scope}-`));
const root = providedRoot ?? fs.mkdtempSync(path.join(os.tmpdir(), `msd-${runtime}-${scope}-`));
try {
let configDir;
let cwd = process.cwd();
@@ -625,7 +625,7 @@ function runMinimalInstall({ runtime, scope, extraArgs = [], installScript = INS
// (RUNTIME_META.globalSuffix mirrors the registry's getGlobalConfigDir
// for every runtime). The previous `--config-dir <root>` collapsed
// configDir onto HOME, so computePathPrefix emitted bare `$HOME/`
// prefixes and the emitted bytes referenced `$HOME/gsd-core/…` — a path
// prefixes and the emitted bytes referenced `$HOME/msd-core/…` — a path
// no real install produces — leaving every emitted-artifact gate
// (ADR-2719 differential, install-tree fixtures, the 19-family baseline)
// blind to drift confined to the real global shape (#3544 evidence: 54
@@ -704,21 +704,21 @@ function manifestSkillSet(manifest) {
const out = new Set();
for (const key of Object.keys(manifest.files)) {
if (key.startsWith('skills/')) {
const seg = key.split('/')[1].replace(/^gsd-/, '').replace(/\.md$/, '');
const seg = key.split('/')[1].replace(/^msd-/, '').replace(/\.md$/, '');
out.add(seg);
} else if (key.startsWith('command/')) {
// OpenCode/Kilo: command/gsd-<cmd>.md
// OpenCode/Kilo: command/msd-<cmd>.md
const file = key.split('/')[1];
out.add(file.replace(/^gsd-/, '').replace(/\.md$/, ''));
} else if (key.startsWith('commands/gsd/')) {
// Gemini: commands/gsd/<cmd>.toml (nested, colon-namespaced)
out.add(file.replace(/^msd-/, '').replace(/\.md$/, ''));
} else if (key.startsWith('commands/msd/')) {
// Gemini: commands/msd/<cmd>.toml (nested, colon-namespaced)
const file = key.split('/')[2];
out.add(file.replace(/\.(md|toml)$/, ''));
} else if (key.startsWith('commands/') && key.split('/').length === 2) {
// Claude local (#1367 fix): flat commands/gsd-<cmd>.md
// Claude local (#1367 fix): flat commands/msd-<cmd>.md
const file = key.split('/')[1];
if (file.startsWith('gsd-') && file.endsWith('.md')) {
out.add(file.replace(/^gsd-/, '').replace(/\.md$/, ''));
if (file.startsWith('msd-') && file.endsWith('.md')) {
out.add(file.replace(/^msd-/, '').replace(/\.md$/, ''));
}
}
}
@@ -731,7 +731,7 @@ function manifestAgentCount(manifest) {
}
/**
* Collect gsd-* skill/command basenames actually present on disk under configDir.
* Collect msd-* skill/command basenames actually present on disk under configDir.
*
* @param {string} configDir
* @param {string} [runtime] - when provided, the skills-kind destination is
@@ -753,35 +753,35 @@ function collectSkillBasenamesOnDisk(configDir, runtime, scope = 'global') {
}
if (fs.existsSync(skillsDir)) {
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
if (entry.isDirectory() && entry.name.startsWith('gsd-')) {
out.add(entry.name.replace(/^gsd-/, ''));
} else if (entry.isFile() && entry.name.startsWith('gsd-') && entry.name.endsWith('.md')) {
out.add(entry.name.replace(/^gsd-/, '').replace(/\.md$/, ''));
if (entry.isDirectory() && entry.name.startsWith('msd-')) {
out.add(entry.name.replace(/^msd-/, ''));
} else if (entry.isFile() && entry.name.startsWith('msd-') && entry.name.endsWith('.md')) {
out.add(entry.name.replace(/^msd-/, '').replace(/\.md$/, ''));
}
}
}
const commandDir = path.join(configDir, 'command');
if (fs.existsSync(commandDir)) {
for (const file of fs.readdirSync(commandDir)) {
if (file.startsWith('gsd-') && file.endsWith('.md')) {
out.add(file.replace(/^gsd-/, '').replace(/\.md$/, ''));
if (file.startsWith('msd-') && file.endsWith('.md')) {
out.add(file.replace(/^msd-/, '').replace(/\.md$/, ''));
}
}
}
const commandsGsdDir = path.join(configDir, 'commands', 'gsd');
if (fs.existsSync(commandsGsdDir)) {
for (const file of fs.readdirSync(commandsGsdDir)) {
const commandsMsdDir = path.join(configDir, 'commands', 'msd');
if (fs.existsSync(commandsMsdDir)) {
for (const file of fs.readdirSync(commandsMsdDir)) {
if (file.endsWith('.md') || file.endsWith('.toml')) {
out.add(file.replace(/\.(md|toml)$/, ''));
}
}
}
// Claude local (#1367 fix): flat gsd-*.md files at commands/ level
// Claude local (#1367 fix): flat msd-*.md files at commands/ level
const flatCommandsDir = path.join(configDir, 'commands');
if (fs.existsSync(flatCommandsDir)) {
for (const file of fs.readdirSync(flatCommandsDir)) {
if (file.startsWith('gsd-') && file.endsWith('.md')) {
out.add(file.replace(/^gsd-/, '').replace(/\.md$/, ''));
if (file.startsWith('msd-') && file.endsWith('.md')) {
out.add(file.replace(/^msd-/, '').replace(/\.md$/, ''));
}
}
}

View File

@@ -7,9 +7,9 @@
* `tests/inventory-manifest-sync.test.cjs`. This module supplies the second
* comparison that test runs: every manifest entry must also have a hand-written row
* in `docs/INVENTORY.md`, which calls itself "Authoritative roster of every shipped
* GSD surface" (`docs/INVENTORY.md:3`) and promises "a new file without a matching
* MSD surface" (`docs/INVENTORY.md:3`) and promises "a new file without a matching
* row here will fail CI" (`docs/INVENTORY.md:681`). Until #3762 neither claim was
* true: PR #3758 shipped `gsd-core/references/planner-coupling.md` with a manifest
* true: PR #3758 shipped `msd-core/references/planner-coupling.md` with a manifest
* entry, no roster row, and fully green CI.
*
* Everything here is PURE — text and the manifest object in, findings out, no
@@ -45,10 +45,10 @@
* `host-integration-adapters/imperative-hook-bus.cjs` row must not satisfy the
* separate top-level `hook-bus.cjs` entry, which is genuinely unrostered.
* • Subsections (`###`), column count, column order, CRLF, and trailing HTML
* comments (`| … |<!-- gsd-allow-legacy-name -->`) are all irrelevant.
* comments (`| … |<!-- msd-allow-legacy-name -->`) are all irrelevant.
* • COMMANDS MATCH ON THEIR SOURCE LINK, not their rendered name. Six namespace
* routers deliberately render `/gsd-workflow` while sourcing
* `commands/gsd/ns-workflow.md`; the display value is not the file identity.
* routers deliberately render `/msd-workflow` while sourcing
* `commands/msd/ns-workflow.md`; the display value is not the file identity.
* `docs/INVENTORY.md:63` documents every command row as carrying "a link to the
* source file", so the link is both the documented shape and the only
* collision-free key.
@@ -70,9 +70,9 @@ const ROSTER_SECTIONS = Object.freeze({
hooks: 'Hooks',
});
/** A manifest `commands` entry (`/gsd-ns-workflow`) → its source basename (`ns-workflow.md`). */
/** A manifest `commands` entry (`/msd-ns-workflow`) → its source basename (`ns-workflow.md`). */
function commandSourceBasename(entry) {
return entry.replace(/^\/gsd-/, '') + '.md';
return entry.replace(/^\/msd-/, '') + '.md';
}
/**
@@ -183,7 +183,7 @@ function tableCells(lines) {
}
/**
* Basenames of every `commands/gsd/<file>.md` markdown-link target in `lines`,
* Basenames of every `commands/msd/<file>.md` markdown-link target in `lines`,
* regardless of how many `../` hops the relative path takes or whether it carries a
* `#anchor` / `?query`.
*/
@@ -194,7 +194,7 @@ function commandSourceLinks(lines) {
let m;
while ((m = linkRe.exec(line)) !== null) {
const target = m[1].split('#')[0].split('?')[0];
const hit = /(?:^|\/)commands\/gsd\/([^/]+\.md)$/.exec(target);
const hit = /(?:^|\/)commands\/msd\/([^/]+\.md)$/.exec(target);
if (hit) basenames.add(hit[1]);
}
}
@@ -252,7 +252,7 @@ function formatRosterFailure({ missingSections, missingRows }) {
missingRows.map((e) => ' ! ' + e).join('\n') +
'\nAdd a row to the matching "## " section of docs/INVENTORY.md: the file name in a cell,\n' +
'plus the one-line role that section\'s table asks for. For a command, the row\'s Source\n' +
'column must link to ../commands/gsd/<file>.md — that link, not the rendered /gsd-name,\n' +
'column must link to ../commands/msd/<file>.md — that link, not the rendered /msd-name,\n' +
'is what this gate reads. Regenerating the manifest does NOT satisfy this: the roster row\n' +
'is hand-written by design, because a role sentence cannot be generated.'
: '',

View File

@@ -1,21 +1,21 @@
// allow-test-rule: source-text-is-the-product
// commands/gsd/*.md files ARE the deployed registry — reading their frontmatter
// commands/msd/*.md files ARE the deployed registry — reading their frontmatter
// validates the structural contract of the command surface, not application source.
'use strict';
/**
* live-command-registry.cjs
*
* Derives the canonical set of live slash-command tokens from the source-of-truth
* registry: commands/gsd/*.md (one file per registered command).
* registry: commands/msd/*.md (one file per registered command).
*
* Each command file has YAML frontmatter with a `name:` field:
* name: gsd:slug (colon-style — most commands)
* name: gsd-slug (dash-style — ns-* namespace commands)
* name: msd:slug (colon-style — most commands)
* name: msd-slug (dash-style — ns-* namespace commands)
*
* For each slug, three canonical token forms are emitted:
* /gsd-slug — Claude / non-Gemini runtimes
* /gsd:slug — Gemini runtime
* $gsd-slug — Codex runtime
* /msd-slug — Claude / non-Gemini runtimes
* /msd:slug — Gemini runtime
* $msd-slug — Codex runtime
*
* The result is memoized per process — a single fs walk is amortized across
* all test files that import this helper. The cache is intentionally not
@@ -31,7 +31,7 @@
const fs = require('node:fs');
const path = require('node:path');
const COMMANDS_DIR = path.join(__dirname, '..', '..', 'commands', 'gsd');
const COMMANDS_DIR = path.join(__dirname, '..', '..', 'commands', 'msd');
// Module-level memoization — set on first call, reused thereafter.
let _cache = null;
@@ -45,13 +45,13 @@ let _cache = null;
* `${prefix}${slug}` for one of these three prefixes — see the `tokens.add`
* calls in `getLiveCommandTokens()` below, which is the sole producer.
*/
const LIVE_COMMAND_TOKEN_PREFIXES = Object.freeze(['/gsd-', '/gsd:', '$gsd-']);
const LIVE_COMMAND_TOKEN_PREFIXES = Object.freeze(['/msd-', '/msd:', '$msd-']);
/**
* Extract the first whitespace-delimited "word" of a string, e.g.
* `"/gsd-plan-phase 2"` -> `"/gsd-plan-phase"`. Real command-token payloads
* `"/msd-plan-phase 2"` -> `"/msd-plan-phase"`. Real command-token payloads
* carry trailing arguments (`init`'s `recommended_actions[].command` is
* literally `/gsd-plan-phase 2`), so an exact-match test against the WHOLE
* literally `/msd-plan-phase 2`), so an exact-match test against the WHOLE
* string would reject every argument-carrying token — the token itself is
* always the first word.
*
@@ -75,8 +75,8 @@ function firstToken(value) {
* exemption. First it was keyed on the leaf name `command`; then on a bare
* `String.startsWith` prefix test with an unconstrained remainder, which
* exempted any string merely SHARING A PREFIX with a real token — e.g.
* `/gsd-x/../../../Users/someone/.ssh/id_rsa` or `/gsd:/etc/passwd` both
* satisfied `startsWith('/gsd-')`/`startsWith('/gsd:')`).
* `/msd-x/../../../Users/someone/.ssh/id_rsa` or `/msd:/etc/passwd` both
* satisfied `startsWith('/msd-')`/`startsWith('/msd:')`).
*
* Deliberately NOT a `startsWith` fast path over `LIVE_COMMAND_TOKEN_PREFIXES`
* short-circuiting this check: exact membership of the first word is the
@@ -102,10 +102,10 @@ function isLiveCommandToken(value, liveTokens) {
* introducing a YAML parser dependency would be disproportionate.
*
* Supported name forms:
* name: gsd:slug → slug = "slug"
* name: gsd-slug → slug = "slug"
* name: "gsd:slug" → slug = "slug" (quoted)
* name: "gsd-slug" → slug = "slug" (quoted)
* name: msd:slug → slug = "slug"
* name: msd-slug → slug = "slug"
* name: "msd:slug" → slug = "slug" (quoted)
* name: "msd-slug" → slug = "slug" (quoted)
*/
function parseSlug(content, filePath) {
// Frontmatter must start with '---' on the very first line.
@@ -126,22 +126,22 @@ function parseSlug(content, filePath) {
const frontmatter = content.slice(0, closingIdx);
// Match `name:` line, allowing optional quotes around the value.
// The value must be one of: gsd:<slug> or gsd-<slug>
// The value must be one of: msd:<slug> or msd-<slug>
// where slug = [a-z0-9][a-z0-9-]*
const nameMatch = frontmatter.match(/^name:\s*"?(gsd[:‑-])([a-z0-9][a-z0-9-]*)"?\s*$/m);
const nameMatch = frontmatter.match(/^name:\s*"?(msd[:‑-])([a-z0-9][a-z0-9-]*)"?\s*$/m);
if (!nameMatch) {
throw new Error(
`[live-command-registry] ${filePath}: could not extract slug from frontmatter ` +
`(expected "name: gsd:<slug>" or "name: gsd-<slug>")`
`(expected "name: msd:<slug>" or "name: msd-<slug>")`
);
}
return nameMatch[2]; // the slug after "gsd:" or "gsd-"
return nameMatch[2]; // the slug after "msd:" or "msd-"
}
/**
* Returns the Set<string> of all canonical slash-command tokens derived from
* commands/gsd/*.md. Memoized — safe to call repeatedly without extra fs I/O.
* commands/msd/*.md. Memoized — safe to call repeatedly without extra fs I/O.
*
* Throws on the first malformed file (fail-loud per CONTEXT.md k302) so
* registry drift is caught immediately rather than silently producing an
@@ -176,9 +176,9 @@ function getLiveCommandTokens() {
const slug = parseSlug(content, filePath);
// Emit all three canonical token forms per slug.
tokens.add(`/gsd-${slug}`); // Claude / non-Gemini
tokens.add(`/gsd:${slug}`); // Gemini
tokens.add(`$gsd-${slug}`); // Codex
tokens.add(`/msd-${slug}`); // Claude / non-Gemini
tokens.add(`/msd:${slug}`); // Gemini
tokens.add(`$msd-${slug}`); // Codex
}
_cache = tokens;

View File

@@ -4,30 +4,30 @@
*
* Single source of truth for the concrete-skill → namespace-router map and the
* nested SKILL.md path layout. The map is DERIVED at require-time by parsing
* each commands/gsd/ns-*.md router's `requires:` frontmatter list with the
* each commands/msd/ns-*.md router's `requires:` frontmatter list with the
* production parser (install-profiles.parseRequires) — never hand-maintained.
*/
const fs = require('node:fs');
const path = require('node:path');
const { parseRequires } = require('../../gsd-core/bin/lib/install-profiles.cjs');
const { parseRequires } = require('../../msd-core/bin/lib/install-profiles.cjs');
const COMMANDS_GSD = path.join(__dirname, '..', '..', 'commands', 'gsd');
const COMMANDS_MSD = path.join(__dirname, '..', '..', 'commands', 'msd');
// Router stems (ns-*.md basenames), discovered from disk and sorted.
const ROUTER_STEMS = fs
.readdirSync(COMMANDS_GSD)
.readdirSync(COMMANDS_MSD)
.filter((f) => f.startsWith('ns-') && f.endsWith('.md'))
.map((f) => f.replace(/\.md$/, ''))
.sort();
/**
* Read a router's `requires:` child list from commands/gsd/<routerStem>.md
* Read a router's `requires:` child list from commands/msd/<routerStem>.md
* using the production frontmatter parser.
*/
function routerChildren(routerStem) {
const srcFile = path.join(COMMANDS_GSD, `${routerStem}.md`);
const srcFile = path.join(COMMANDS_MSD, `${routerStem}.md`);
return parseRequires(fs.readFileSync(srcFile, 'utf8'));
}
@@ -47,8 +47,8 @@ for (const routerStem of ROUTER_STEMS) {
/**
* Nested SKILL.md path for a concrete skill stem:
* <skillsRoot>/<prefix><router>/skills/<stem>/SKILL.md
* prefix is '' for runtimes that nest under a skills/gsd parent dir (hermes),
* or 'gsd-' for flat-prefixed runtimes (claude, cline, qwen, …).
* prefix is '' for runtimes that nest under a skills/msd parent dir (hermes),
* or 'msd-' for flat-prefixed runtimes (claude, cline, qwen, …).
*/
function nestedSkillPath(skillsRoot, prefix, stem) {
const router = CHILD_ROUTER[stem];
@@ -57,7 +57,7 @@ function nestedSkillPath(skillsRoot, prefix, stem) {
}
module.exports = {
COMMANDS_GSD,
COMMANDS_MSD,
ROUTER_STEMS,
CHILD_ROUTER,
routerChildren,

View File

@@ -9,7 +9,7 @@
* ── The overlay technique ────────────────────────────────────────────────
*
* A test that needs a spawned `bin/install.js` to read a DIFFERENT
* `gsd-core/workflows/execute-phase.md` (or any other repo file) than this
* `msd-core/workflows/execute-phase.md` (or any other repo file) than this
* checkout's real one, without paying to copy the ~400 MB repository (mostly
* node_modules) for every run, calls `buildOverlayRepo` with a map of
* POSIX-relative-path -> replacement content. `buildOverlayRepo` mirrors the
@@ -216,7 +216,7 @@ function buildOverlayRepo(fileOverrides, opts = {}) {
// synthetic root instead of walking the whole repo; every existing caller
// uses the default REPO_ROOT and is unaffected.
const root = opts.root || REPO_ROOT;
const tmpRepo = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-2930-overlay-'));
const tmpRepo = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-2930-overlay-'));
const entries = Object.entries(fileOverrides).map(([relPath, content]) => ({
parts: relPath.split('/'),
content,

View File

@@ -27,7 +27,7 @@
* An invocation that reaches is a violation UNLESS it sits inside an open
* `commit_docs` conditional in the SAME fenced block, carries an exclude
* pathspec naming `.planning`, or the line carries a tracked
* `# gsd-scan-ignore: #NNN` declaration (shared machinery — see
* `# msd-scan-ignore: #NNN` declaration (shared machinery — see
* tests/helpers/shipped-command-scan.cjs).
*
* ## Known limits
@@ -37,7 +37,7 @@
* `.planning/`-reaching stage by a contributor editing shipped content, not
* a determined attempt to defeat it. An isolated security review (#3585)
* empirically confirmed the following shapes stage `.planning/` at runtime
* while scoring ZERO offenders here, and none of them is a shape GSD
* while scoring ZERO offenders here, and none of them is a shape MSD
* workflow content actually uses — chasing them means writing a shell
* interpreter:
* - `eval "git add -A"` — the invocation lives inside a string literal
@@ -318,7 +318,7 @@ const scanText = (file, text) => {
continue;
}
// A `# gsd-scan-ignore:` attempt with no tracking reference is a
// A `# msd-scan-ignore:` attempt with no tracking reference is a
// malformed declaration — reported on its own terms, never silently
// folded into "unguarded" (see shipped-command-scan.cjs's declaration
// comment for why the diagnosis must be specific).
@@ -361,8 +361,8 @@ const scanText = (file, text) => {
// The repo-wide walk: every `.md` file (recursive) under each scan root.
const SCAN_ROOTS = [
'gsd-core/workflows',
'gsd-core/references',
'msd-core/workflows',
'msd-core/references',
'agents',
'commands',
'skills',

View File

@@ -2,9 +2,9 @@
/**
* Parsing seam for `tests/pr-branch-planning-filter.test.cjs` against
* `/gsd-pr-branch`'s planning-path filter.
* `/msd-pr-branch`'s planning-path filter.
*
* The workflow (`gsd-core/workflows/pr-branch.md`) is the single source of
* The workflow (`msd-core/workflows/pr-branch.md`) is the single source of
* truth for which `.planning/` subdirectories are "transient" (excluded from
* a PR branch in non-strict mode), for the "structural" regex that carves
* out an exception even in that non-strict filter, AND for the
@@ -34,9 +34,9 @@
const fs = require('fs');
const path = require('path');
const { escapeRegex: escapeRe } = require('../../gsd-core/bin/lib/pattern.cjs');
const { escapeRegex: escapeRe } = require('../../msd-core/bin/lib/pattern.cjs');
const WORKFLOW_PATH = path.join(__dirname, '..', '..', 'gsd-core', 'workflows', 'pr-branch.md');
const WORKFLOW_PATH = path.join(__dirname, '..', '..', 'msd-core', 'workflows', 'pr-branch.md');
const TRANSIENT_DIRS_RE = /^\s*TRANSIENT_DIRS="([^"]*)"/gm;
const STRUCTURAL_RE_RE = /^\s*STRUCTURAL_RE="([^"]*)"/gm;

View File

@@ -4,10 +4,10 @@
* Process seam — the single spawnSync-based primitive test helpers use to
* run a subprocess and get back a typed, discriminated-union result.
*
* Design contract: .gsd/phase/test-3055-process-seam-module/40-design.md
* Test matrix: .gsd/phase/test-3055-process-seam-module/50-test-matrix.md
* Design contract: .msd/phase/test-3055-process-seam-module/40-design.md
* Test matrix: .msd/phase/test-3055-process-seam-module/50-test-matrix.md
*
* Scope (Phase 1, #3055): this module and the `runGsdTools` adapter in
* Scope (Phase 1, #3055): this module and the `runMsdTools` adapter in
* tests/helpers.cjs. The 23 local wrapper helpers are migrated in a later
* wave — they are not touched here.
*
@@ -33,7 +33,7 @@
* `exitCode: null` under an EXITED outcome, an incoherent shape, and
* would silently drop the #969 kill-discrimination retry for the exact
* case it exists to catch. KILLED is reported as its own outcome so the
* `runGsdTools` adapter can retry it exactly like TIMED_OUT.
* `runMsdTools` adapter can retry it exactly like TIMED_OUT.
* - `status !== null` with a populated `result.error` -> EXITED, evidence
* over classification, checked before any error-code branch below. At
* the exact timeout boundary, spawnSync can report `error.code ===

View File

@@ -19,7 +19,7 @@
* (`tests/helpers/process-seam.cjs`'s `runNode` forwards extra argv
* verbatim, ahead of the target script) so the patch lands in the require
* cache BEFORE `bin/install.js`'s own
* `require('../gsd-core/bin/lib/install-shadow-report.cjs')` resolves.
* `require('../msd-core/bin/lib/install-shadow-report.cjs')` resolves.
*
* One-shot subprocess: no restoration needed — the process exits right
* after the single `install()` call this preload targets. This is NOT a
@@ -35,7 +35,7 @@
const path = require('node:path');
const modPath = require.resolve(
path.join(__dirname, '..', '..', 'gsd-core', 'bin', 'lib', 'install-shadow-report.cjs'),
path.join(__dirname, '..', '..', 'msd-core', 'bin', 'lib', 'install-shadow-report.cjs'),
);
const shadowReportModule = require(modPath);

View File

@@ -1,7 +1,7 @@
'use strict';
const { scanFencedBlocks } = require('../../gsd-core/bin/lib/markdown-sectionizer.cjs');
const { splitLines } = require('../../gsd-core/bin/lib/text-lines.cjs');
const { scanFencedBlocks } = require('../../msd-core/bin/lib/markdown-sectionizer.cjs');
const { splitLines } = require('../../msd-core/bin/lib/text-lines.cjs');
/**
* Fold unquoted shell backslash-newline continuations into logical lines.

View File

@@ -5,7 +5,7 @@
* commit_docs guard consumes ONE shell tokenizer rather than hand-rolling a
* second. Only the primitives BOTH guards need live here — the shell
* tokenizer, the bare-command-name extraction, the -c payload search, and
* the `gsd-scan-ignore:` declaration machinery.
* the `msd-scan-ignore:` declaration machinery.
*
* Commit-specific logic stays in the test file and is deliberately NOT
* moved here: `isCommitInvocation`, `hasScopedFiles`, `SYNOPSIS_TOKEN_RE`,
@@ -14,11 +14,11 @@
* scan and do not generalize to other guards.
*/
// 443 live substitution sites are `VAR=$(gsd_run …)`. That is the majority
// 443 live substitution sites are `VAR=$(msd_run …)`. That is the majority
// and it is the wrong generalisation — the remaining six are neither, and
// they are live today:
// for REVIEW_FLAG in $(gsd_run review-lane flags) (×3)
// ${PLAN_PRE_HOOKS_JSON:-$(gsd_run loop render-hooks …)} (×3)
// for REVIEW_FLAG in $(msd_run review-lane flags) (×3)
// ${PLAN_PRE_HOOKS_JSON:-$(msd_run loop render-hooks …)} (×3)
// A parameter-expansion default glues just as hard as an assignment, and so
// does ordinary text (`pre$(…)`) and an indexed assignment (`A[0]=$(…)`).
// Enumerating prefixes means a new one every time the shell is used
@@ -38,13 +38,13 @@
// this regex rather than of the grammar and the next edit could lose it.
// THE STRIP IS REFUSED ON A TOKEN THAT CARRIED A QUOTE, because quoting is
// exactly what decides whether an opener is syntax or data. `printf %s
// '$(gsd_run' query commit fixup` runs no gsd command — the opener is
// single-quoted literal text — but quote removal leaves the token `$(gsd_run`
// '$(msd_run' query commit fixup` runs no msd command — the opener is
// single-quoted literal text — but quote removal leaves the token `$(msd_run`
// and a strip would then read it as a binary, fabricating an invocation out
// of a string. The tokenizer already knows; it just was not saying.
//
// RESIDUAL, named rather than implied: this closes the strip's exposure, not
// the tokenizer's quote-blindness in general. `printf %s 'gsd_run' query
// the tokenizer's quote-blindness in general. `printf %s 'msd_run' query
// commit fixup` still reads as an invocation, because no markup is stripped
// there — the token's value simply IS the binary name. That predates this
// rule and is unchanged by it; closing it needs quote provenance carried
@@ -54,7 +54,7 @@ const bareCommandName = (t) => {
const mask = t.qmask || '0'.repeat(t.value.length);
// The LAST opener whose own two characters were unquoted. Per-character,
// not per-token: a quote elsewhere in the word protects only itself, and
// `echo "pre"$(gsd_run …)` really does run.
// `echo "pre"$(msd_run …)` really does run.
for (let i = t.value.length - 2; i >= 0; i -= 1) {
if (t.value[i] === '$' && t.value[i + 1] === '(' && mask[i] === '0' && mask[i + 1] === '0') {
return t.value.slice(i + 2);
@@ -69,15 +69,15 @@ const bareCommandName = (t) => {
// parity walk counted only `"`, so a single-quoted message was transparent
// to it in both directions), backslash escapes, and unquoted control
// operators — which become their own tokens, so an operator glued to its
// neighbour (`"a"&&gsd_run`) still separates. Quoted operators and quoted
// neighbour (`"a"&&msd_run`) still separates. Quoted operators and quoted
// whitespace stay literal, which is what keeps a command substitution inside
// a message ("$(gsd-tools query phase-list)") from reading as a second
// a message ("$(msd-tools query phase-list)") from reading as a second
// invocation, with no separator lookahead needed.
//
// Beyond the operators, the shell consumes THREE more things before argv
// exists, and each one previously leaked into the token list as a value —
// so `--files >/dev/null 2>&1 || true` (a live tail shape in
// gsd-core/references/execute-phase-requirement-revert.md) scored as scoped
// msd-core/references/execute-phase-requirement-revert.md) scored as scoped
// while the runtime saw files=[] and took the blanket-.planning/ default:
// - a single `&` is a control operator like `;` (`&&` is checked first);
// - an unquoted `#` at the START of a word begins a comment and ends the
@@ -112,7 +112,7 @@ const tokenize = (str) => {
// A PER-CHARACTER quote mask, parallel to `cur`: '1' where the character was
// quoted or backslash-escaped (i.e. protected, therefore data), '0' where it
// was bare syntax. A per-TOKEN boolean is the obvious shape and it is wrong
// in the unaffordable direction — `echo "pre"$(gsd_run …)` executes, and a
// in the unaffordable direction — `echo "pre"$(msd_run …)` executes, and a
// token-wide flag suppresses the strip on it, turning a false positive into
// a silent false negative.
let curMask = '';
@@ -212,7 +212,7 @@ const commentPortion = (line) => {
//
// IT IS ONLY A MARKER IN COMMENT POSITION. Matching the token anywhere on
// the line let a commit MESSAGE carry it — `commit "docs: explain
// gsd-scan-ignore: semantics"` — and silently exempt a real offender. A
// msd-scan-ignore: semantics"` — and silently exempt a real offender. A
// false-negative escape hatch is the one thing this file must not ship, and
// an exemption keyed to attacker-controlled-looking text is precisely that.
// AND IT MUST CARRY A TRACKING ISSUE. An exemption whose reason is free text
@@ -239,10 +239,10 @@ const ISSUE_REF_RE = /#\d+|https?:\/\//;
// declaration must not fail as a MYSTERY: left un-exempt and nothing more,
// its author is told their commit is unscoped — a true statement about a line
// they had already explained, which is the mangle-until-it-shuts-up loop the
// marker exists to prevent. `# gsd-scan-ignore:` with an empty reason is the
// marker exists to prevent. `# msd-scan-ignore:` with an empty reason is the
// likeliest way to get this wrong and so is the case that most needs the
// specific diagnosis; a `\S`-requiring pattern silently drops it.
const SCAN_IGNORE_REASON_RE = /gsd-scan-ignore:(.*)$/;
const SCAN_IGNORE_REASON_RE = /msd-scan-ignore:(.*)$/;
const declarationReason = (portion) => {
const m = SCAN_IGNORE_REASON_RE.exec(portion);
return m === null ? null : m[1];
@@ -259,7 +259,7 @@ const declarationReason = (portion) => {
// The marker's reason, but only when the marker is in COMMENT position and
// survives neither as an argv token. Returns null when there is no attempt.
const declaredReasonFor = (line) => {
if (tokenize(line).some((t) => /gsd-scan-ignore:/.test(t.value))) return null;
if (tokenize(line).some((t) => /msd-scan-ignore:/.test(t.value))) return null;
return declarationReason(commentPortion(line));
};
const isDeclared = (line) => {
@@ -276,11 +276,11 @@ const isUntrackedDeclaration = (line) => {
// A shell invoked with -c runs its next argument AS A COMMAND, so the
// invocation lives inside a quoted token and no amount of markup-stripping
// reaches it: `bash -c "gsd_run query commit fixup"` scored zero candidates
// reaches it: `bash -c "msd_run query commit fixup"` scored zero candidates
// while being perfectly executable. The recursion is keyed on the INVOKER,
// not on "a quoted token that parses as an invocation" — that wider rule
// would flag a commit MESSAGE quoting a command, a false positive against
// ordinary documentation. Here the outer command is bash, so a gsd commit
// ordinary documentation. Here the outer command is bash, so a msd commit
// message can never reach it.
// The invoker set, widened past the four spellings the first cut guessed at.
// `ash`, `csh`, `tcsh`, `fish` and `yash` all take -c and all run what
@@ -293,7 +293,7 @@ const shellDashCPayloads = (str) => {
const drain = () => {
if (!group.length) { return; }
// THE INVOKER MUST BE THE COMMAND, not merely present in the segment.
// Searching anywhere made `echo bash -c "gsd_run query commit fixup"` a
// Searching anywhere made `echo bash -c "msd_run query commit fixup"` a
// candidate — `echo` prints the string, it does not run it — which is a
// false positive against prose that merely quotes a command line. Only an
// env assignment, a shell keyword, or a list/prompt marker may precede the

View File

@@ -160,7 +160,7 @@ const FIXTURE_HOOK_TIMEOUT_SECONDS = 5;
const STAGED_HOOK_SCRIPT_TIMEOUT_MS = 20000;
/**
* A single `gsd-tools.cjs` CLI subcommand invocation, spawned directly (never
* A single `msd-tools.cjs` CLI subcommand invocation, spawned directly (never
* via an intermediate shell script), used by a loop/hook-point e2e test. The
* command does real in-process work -- capability/config resolution, project
* scaffolding, or (for the `check` verbs that inspect git-tracked state) real
@@ -170,12 +170,12 @@ const STAGED_HOOK_SCRIPT_TIMEOUT_MS = 20000;
* norm's 7 sites: `loop render-hooks <point>`, `check <check-id>`,
* `execute <hook-point>`, and `init new-project` (tests/loop-walk.qa.test.cjs's
* concurrency test) -- the norm describes the CALL SHAPE (single direct
* gsd-tools.cjs spawn, no fan-out), not one specific verb family, so this list
* msd-tools.cjs spawn, no fan-out), not one specific verb family, so this list
* is illustrative, not exhaustive.
*
* Distinct from `HOOK_FANOUT_TIMEOUT_MS` (a bash-hosted script that itself
* shells out to further subprocesses) despite the coincidentally-matching
* value: this norm's sites spawn `gsd-tools.cjs` and nothing beneath it.
* value: this norm's sites spawn `msd-tools.cjs` and nothing beneath it.
* Distinct from `PROBE_TIMEOUT_MS`: the same underlying `loop render-hooks`
* command is ALSO called at `PROBE_TIMEOUT_MS` (15000ms) elsewhere in the
* suite against lighter temp-project fixtures -- the two bounds are kept
@@ -211,7 +211,7 @@ const LOOP_HOOK_POINT_CLI_TIMEOUT_MS = 60000;
const SCAN_USAGE_ERROR_TIMEOUT_MS = 5000;
/**
* A Node hook script (gsd-prompt-guard-hook.js, gsd-read-injection-scanner.js)
* A Node hook script (msd-prompt-guard-hook.js, msd-read-injection-scanner.js)
* spawned directly and fed malformed JSON on stdin -- expected to fail closed
* gracefully, no subprocess fan-out, no real scan work. A different operation
* family from `SCAN_USAGE_ERROR_TIMEOUT_MS` (a bash scan script given no
@@ -282,7 +282,7 @@ const REAL_REPO_GIT_TIMEOUT_MS = 30000;
const PATHOLOGICAL_INPUT_TEST_TIMEOUT_MS = 60000;
/**
* A single `gsd-tools.cjs` CLI subcommand invocation, spawned directly
* A single `msd-tools.cjs` CLI subcommand invocation, spawned directly
* (execFileSync/spawnSync, or the process seam's runNode wrapping the same
* shape -- never via an intermediate shell script), with no confirmed
* nested-subprocess fan-out. A distinct, heavier tier than
@@ -298,7 +298,7 @@ const PATHOLOGICAL_INPUT_TEST_TIMEOUT_MS = 60000;
* migration, epic #4445 -- every site independently arrived at this exact
* value -- that is why it lives here rather than as a file-local constant.
*/
const GSD_TOOLS_CLI_MODERATE_TIMEOUT_MS = 30000;
const MSD_TOOLS_CLI_MODERATE_TIMEOUT_MS = 30000;
/**
* NOT a subprocess spawn timeout. Fixture DATA -- the default, plausible-
@@ -348,7 +348,7 @@ module.exports = {
GENERATOR_SCRIPT_TIMEOUT_MS,
REAL_REPO_GIT_TIMEOUT_MS,
PATHOLOGICAL_INPUT_TEST_TIMEOUT_MS,
GSD_TOOLS_CLI_MODERATE_TIMEOUT_MS,
MSD_TOOLS_CLI_MODERATE_TIMEOUT_MS,
TASK_RESOLVER_INVOKE_TIMEOUT_MS,
HTTP_REACHABLE_PROBE_TIMEOUT_FIXTURE_MS,
};