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

@@ -26,7 +26,7 @@ const PR_FULL_SUITES = ['unit', 'integration', 'security'];
// Relative to repoRoot. We walk these to discover SUT-internal requires so that
// a change to a deep helper propagates through re-export chains to tests.
const SOURCE_TREES = [
'gsd-core/bin/lib',
'msd-core/bin/lib',
'bin/lib',
'bin',
'scripts',
@@ -433,7 +433,7 @@ function runSuite(repoRoot, suite) {
}
function resolveBaseRef() {
if (process.env.GSD_AFFECTED_BASE) return process.env.GSD_AFFECTED_BASE;
if (process.env.MSD_AFFECTED_BASE) return process.env.MSD_AFFECTED_BASE;
if (process.env.GITHUB_BASE_REF) return `origin/${process.env.GITHUB_BASE_REF}`;
return 'origin/main';
}

View File

@@ -3,7 +3,7 @@
/**
* Post-install path audit for workflow-invoked scripts (#2995).
*
* Walks workflowsDir, extracts every `${GSD_HOME[...]}/<path>.<cjs|js|sh>`
* Walks workflowsDir, extracts every `${MSD_HOME[...]}/<path>.<cjs|js|sh>`
* token, and asserts:
* 1. the file exists in the repo at that <path> (catches typos)
* 2. <path>'s first segment is in installedPrefixes (catches the
@@ -21,10 +21,10 @@ const AUDIT_FINDING = Object.freeze({
NOT_INSTALLED: 'not_installed',
});
// Match `${GSD_HOME}` or `${GSD_HOME:-...}` followed by a /-rooted path
// Match `${MSD_HOME}` or `${MSD_HOME:-...}` followed by a /-rooted path
// ending in .cjs/.js/.sh. The path is captured verbatim (relative to
// the install root).
const REF_RE = /\$\{GSD_HOME(?::-[^}]*)?\}\/([A-Za-z0-9_./-]+\.(?:cjs|js|sh))/g;
const REF_RE = /\$\{MSD_HOME(?::-[^}]*)?\}\/([A-Za-z0-9_./-]+\.(?:cjs|js|sh))/g;
function listWorkflowFiles(dir) {
if (!fs.existsSync(dir)) return [];

View File

@@ -9,7 +9,7 @@
# scripts/base64-scan.sh --file path/to/file # Scan a single file
# scripts/base64-scan.sh --dir agents/ # Scan all files in a directory
#
# Exit codes (ADR-3889, #3908 — registered in gsd-core/bin/shared/exit-codes.json):
# Exit codes (ADR-3889, #3908 — registered in msd-core/bin/shared/exit-codes.json):
# 0 = clean
# 1 = findings detected
# $EXIT_USAGE (64) = usage error (bad argv, missing --file/--dir target)
@@ -23,7 +23,7 @@ set -euo pipefail
# literal integer, and never let a missing registry silently degrade to
# exit 0.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EXIT_CODES_SH="$SCRIPT_DIR/../gsd-core/bin/shared/exit-codes.sh"
EXIT_CODES_SH="$SCRIPT_DIR/../msd-core/bin/shared/exit-codes.sh"
if [[ ! -f "$EXIT_CODES_SH" ]]; then
echo "base64-scan: FATAL: exit-code registry not found at $EXIT_CODES_SH" >&2
echo " Regenerate with: node scripts/gen-exit-code-registry.cjs --write" >&2

View File

@@ -1,4 +1,4 @@
{
"$comment": "ADR-3180 Decision 4(e) ratchet, owned by Phase 8 (#3218). See scripts/lint-planning-prompt-drift.cjs. SHRINK-ONLY: entries are removed as sites migrate to the gsd-core CLI; new or changed entries fail lint:ci. `count` is the number of byte-identical (file, text) occurrences acknowledged at this site — a run producing fewer fails as a partial migration, more fails as an unacknowledged new copy. Emptied by #3218: all 6 recorded sites (7 occurrences) migrated to `gsd_run query find-phase` — see gsd-core/workflows/{execute-plan,plan-phase,plan-review-convergence,progress}.md.",
"$comment": "ADR-3180 Decision 4(e) ratchet, owned by Phase 8 (#3218). See scripts/lint-planning-prompt-drift.cjs. SHRINK-ONLY: entries are removed as sites migrate to the msd-core CLI; new or changed entries fail lint:ci. `count` is the number of byte-identical (file, text) occurrences acknowledged at this site — a run producing fewer fails as a partial migration, more fails as an unacknowledged new copy. Emptied by #3218: all 6 recorded sites (7 occurrences) migrated to `msd_run query find-phase` — see msd-core/workflows/{execute-plan,plan-phase,plan-review-convergence,progress}.md.",
"entries": []
}

View File

@@ -3,8 +3,8 @@
/**
* Benchmarks the token-count effect of every registered variant-swap pair
* (ADR-4139, Phase 6 #4406 — `gsd-core/workflows/<name>/{modes,steps,templates}/*.compact.md`
* and `gsd-core/templates/**\/*.compact.md`). Sibling to
* (ADR-4139, Phase 6 #4406 — `msd-core/workflows/<name>/{modes,steps,templates}/*.compact.md`
* and `msd-core/templates/**\/*.compact.md`). Sibling to
* `scripts/benchmark-compact-content.cjs` (Phase 3's spine/detail benchmark) rather than an
* extension of it — the two are different data shapes (a variant pair is two independent,
* deliberately-overlapping complete files; a spine/detail split is one document partitioned in
@@ -14,7 +14,7 @@
* For every registered pair it reports the "off" token count (the canonical file — what
* `workflow.compact_content=false`, the default, pays at that call site) against the "on" token
* count (the `.compact.md` sibling — what `workflow.compact_content=true` pays once the gate
* resolves to it). See `gsd-core/references/compact-content-gate.md` §"Streams 1b and 4" for the
* resolves to it). See `msd-core/references/compact-content-gate.md` §"Streams 1b and 4" for the
* resolution rule this measures.
*
* PROXY-TOKENIZER CAVEAT: same as the sibling script — `gpt-tokenizer` is a stand-in tokenizer;
@@ -50,8 +50,8 @@ const ROOT = path.resolve(__dirname, '..');
// (cmdAgentSkills), not a markdown literal reference, but token accounting
// doesn't care how a pair is reached — only that it's registered.
const VARIANT_ROOTS = [
path.join(ROOT, 'gsd-core', 'workflows'),
path.join(ROOT, 'gsd-core', 'templates'),
path.join(ROOT, 'msd-core', 'workflows'),
path.join(ROOT, 'msd-core', 'templates'),
path.join(ROOT, 'agents'),
];
const BASELINE_PATH = path.join(ROOT, 'tests', 'fixtures', 'compact-content-variant-benchmark-baseline.json');

View File

@@ -9,7 +9,7 @@
* what `workflow.compact_content=false` pays today, since an opted-out project's
* spine reads every detail part back in) against the "on" token count (spine
* alone — what `workflow.compact_content=true` pays, since the detail `Read`
* never fires). See `.gsd/phase/enhance-4404-token-benchmark/40-design.md` for
* never fires). See `.msd/phase/enhance-4404-token-benchmark/40-design.md` for
* the full design rationale.
*
* PROXY-TOKENIZER CAVEAT: Anthropic publishes no tokenizer for Claude 3+, so
@@ -63,7 +63,7 @@ const { countTokens } = require('gpt-tokenizer');
const { runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.resolve(__dirname, '..');
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
const WORKFLOWS_DIR = path.join(ROOT, 'msd-core', 'workflows');
const BASELINE_PATH = path.join(ROOT, 'tests', 'fixtures', 'compact-content-benchmark-baseline.json');
// The tokenizer's own package.json is read live (`require.resolve` + a plain

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
* Copy GSD hooks to dist for installation.
* Copy MSD hooks to dist for installation.
* Validates JavaScript syntax before copying to prevent shipping broken hooks.
* See #1107, #1109, #1125, #1161 — a duplicate const declaration shipped
* in dist and caused PostToolUse hook errors for all users.
@@ -24,73 +24,73 @@ const STAGE_DIR = path.join(HOOKS_DIR, `.dist-staging-${process.pid}`);
// Hooks to copy (pure Node.js, no bundling needed)
const HOOKS_TO_COPY = [
'gsd-check-update-worker.js',
'gsd-check-update.js',
'msd-check-update-worker.js',
'msd-check-update.js',
// SessionStart canonical-path bootstrap (#997). In a Claude Code marketplace
// plugin install, ~/.claude/gsd-core is never created, so every
// `@~/.claude/gsd-core/...` include in agents/commands/templates resolves to
// plugin install, ~/.claude/msd-core is never created, so every
// `@~/.claude/msd-core/...` include in agents/commands/templates resolves to
// nothing. This hook symlinks the canonical path's immutable subdirs to the
// plugin's bundled gsd-core/ tree; no-op in classic installs. Must ship to
// plugin's bundled msd-core/ tree; no-op in classic installs. Must ship to
// dist so the installer copies it into the target hooks/ dir.
'gsd-ensure-canonical-path.js',
// Required by gsd-check-update-worker.js at runtime — must ship alongside it
'msd-ensure-canonical-path.js',
// Required by msd-check-update-worker.js at runtime — must ship alongside it
// so require('./managed-hooks-registry.cjs') resolves in the installed hooks/ dir.
'managed-hooks-registry.cjs',
'gsd-context-monitor.js',
'msd-context-monitor.js',
// Cursor lifecycle hooks (#777 + ADR-1239/#2089): 6 managed events
'gsd-cursor-session-start.js',
'gsd-cursor-post-tool.js',
'gsd-cursor-pre-tool.js',
'gsd-cursor-stop.js',
'gsd-cursor-subagent-start.js',
'gsd-cursor-subagent-stop.js',
'msd-cursor-session-start.js',
'msd-cursor-post-tool.js',
'msd-cursor-pre-tool.js',
'msd-cursor-stop.js',
'msd-cursor-subagent-start.js',
'msd-cursor-subagent-stop.js',
// Windsurf/Cascade lifecycle hooks (ADR-1239/#2100 Stage 2): 2 blocking events
'gsd-windsurf-pre-write.js',
'gsd-windsurf-pre-command.js',
// Claude Code FileChanged hook (#770) — hot-reloads gsd config when
'msd-windsurf-pre-write.js',
'msd-windsurf-pre-command.js',
// Claude Code FileChanged hook (#770) — hot-reloads msd config when
// .planning/config.json changes mid-session. Must ship to dist so the
// installer can copy it to the target hooks/ dir and register FileChanged.
'gsd-config-reload.js',
'msd-config-reload.js',
// Agent-dispatch isolation guard (#3045): blocks an executor Agent()
// dispatch missing its harness isolation parameter when the project
// resolves to harness-worktree. Requires the sibling
// gsd-core/bin/lib/{runtime-name-policy,capability-registry}.cjs modules
// at runtime — those ship as part of the full gsd-core/ tree, not via this
// msd-core/bin/lib/{runtime-name-policy,capability-registry}.cjs modules
// at runtime — those ship as part of the full msd-core/ tree, not via this
// list.
'gsd-agent-isolation-guard.js',
'gsd-prompt-guard.js',
'gsd-read-guard.js',
'gsd-read-injection-scanner.js',
'msd-agent-isolation-guard.js',
'msd-prompt-guard.js',
'msd-read-guard.js',
'msd-read-injection-scanner.js',
// Secret-file read guard (#4221) — replaces the installer's Read(.env*) deny rules
'gsd-secret-read-guard.js',
'gsd-statusline.js',
'gsd-update-banner.js',
'gsd-workflow-guard.js',
'gsd-worktree-path-guard.js',
'msd-secret-read-guard.js',
'msd-statusline.js',
'msd-update-banner.js',
'msd-workflow-guard.js',
'msd-worktree-path-guard.js',
// Catastrophic-shrink guard for curated .planning/ artifacts (#2255, fix 3 of #973)
'gsd-write-guard.js',
'msd-write-guard.js',
// Community hooks (bash, opt-in via .planning/config.json hooks.community)
'gsd-session-state.sh',
'gsd-validate-commit.sh',
'gsd-phase-boundary.sh',
'msd-session-state.sh',
'msd-validate-commit.sh',
'msd-phase-boundary.sh',
// Portable node resolver (#3662). Managed JS hook commands under
// --portable-hooks route through it (bash <resolver> <baked-node>
// <script>) so node resolves at hook-fire time in every environment
// sharing the config root. It IS registered in MANAGED_HOOKS
// (managed-hooks-registry.cjs) for staleness tracking like every other
// shipped .sh hook, and install.js stamps its {{GSD_VERSION}} header the
// shipped .sh hook, and install.js stamps its {{MSD_VERSION}} header the
// same way (#4076 — the prior comment here claimed the opposite on both
// counts, which is why the header was missing and staleness detection was
// permanently broken for this file).
'gsd-node-runner.sh',
'msd-node-runner.sh',
// Graphify auto-update hook (#3347 / PR #3557 / #3579). Opt-in via
// .planning/config.json graphify.auto_update; off by default.
'gsd-graphify-update.sh'
'msd-graphify-update.sh'
];
// Subdirectories under hooks/ whose contents must also ship to dist. Each
// entry is copied as `hooks/<dir>/*` → `hooks/dist/<dir>/*` so detached
// helpers (e.g. hooks/lib/gsd-graphify-rebuild.sh) resolve from the hook's
// helpers (e.g. hooks/lib/msd-graphify-rebuild.sh) resolve from the hook's
// installed runtime path. See #3579.
const HOOKS_SUBDIRS_TO_COPY = ['lib'];
@@ -208,7 +208,7 @@ function build() {
// destination — readers (install.js subprocesses spawned by parallel
// install tests) can observe the dest empty or partial mid-write,
// producing flaky failures such as bug-2136 part 4 where installed .sh
// hooks lacked their "# gsd-hook-version:" header. POSIX rename(2)
// hooks lacked their "# msd-hook-version:" header. POSIX rename(2)
// makes the swap atomic so readers see either the old file or the new
// file. The staging file lives outside DIST_DIR so readdirSync(DIST_DIR)
// (in install.js and tests) never observes a transient ".tmp" sibling.

View File

@@ -25,8 +25,8 @@ const { renderGithubReleaseNotes } = require('./github-release-notes.cjs');
const {
compareSemverCore,
isStableTripletSemver,
} = require('../../gsd-core/bin/lib/semver-compare.cjs');
const { packageName, repoSlug: defaultRepoSlug } = require('../../gsd-core/bin/lib/package-identity.cjs');
} = require('../../msd-core/bin/lib/semver-compare.cjs');
const { packageName, repoSlug: defaultRepoSlug } = require('../../msd-core/bin/lib/package-identity.cjs');
function parseArgs(argv) {
const opts = {
@@ -324,7 +324,7 @@ function resolveChangelogPath(opts) {
* 1 — I/O error or missing required flags.
*
* Fix for #3496: provides a deterministic range-aware helper so the
* `/gsd-update` show_changes_and_confirm step no longer relies on
* `/msd-update` show_changes_and_confirm step no longer relies on
* vague/manual extraction that can silently skip intermediate versions.
*/
function cmdExtract(opts) {

View File

@@ -4,7 +4,7 @@ const cp = require('node:child_process');
const path = require('node:path');
const { parseFragment } = require('./parse.cjs');
const { packageName, repoSlug: defaultRepoSlug } = require('../../gsd-core/bin/lib/package-identity.cjs');
const { packageName, repoSlug: defaultRepoSlug } = require('../../msd-core/bin/lib/package-identity.cjs');
const SECTION_ORDER = ['Fixed', 'Added', 'Changed', 'Deprecated', 'Removed', 'Security'];
@@ -19,14 +19,14 @@ const FIXED_GROUPS = [
},
{
title: 'Install & runtime conversion',
pattern: /\b(install|installer|runtime|windows|powershell|codex|gemini|antigravity|hook|hooks|gsd-sdk|sdk readiness|cjs|model-catalog|path|shim)\b/i,
pattern: /\b(install|installer|runtime|windows|powershell|codex|gemini|antigravity|hook|hooks|msd-sdk|sdk readiness|cjs|model-catalog|path|shim)\b/i,
},
];
const REMOVED_GROUPS = [
{
title: 'Intel updater',
pattern: /\b(intel|gsd-intel-updater|layout detection)\b/i,
pattern: /\b(intel|msd-intel-updater|layout detection)\b/i,
},
];

View File

@@ -31,7 +31,7 @@ const OPT_OUT_LABEL = 'no-changelog';
// fragment or an explicit opt-out label. Test/CI/docs/lock files do not.
const USER_FACING_PREFIXES = [
'bin/',
'gsd-core/',
'msd-core/',
'src/',
'agents/',
'commands/',

View File

@@ -7,7 +7,7 @@ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { FAMILIES, ALIAS_TABLE } = require('./lib/alias-drift-families.cjs');
const ROOT = path.resolve(__dirname, '..');
const aliasesPath = path.join(ROOT, 'gsd-core', 'bin', 'lib', `${ALIAS_TABLE}.cjs`);
const aliasesPath = path.join(ROOT, 'msd-core', 'bin', 'lib', `${ALIAS_TABLE}.cjs`);
function fail(message) {
process.stderr.write(`${message}\n`);
@@ -40,7 +40,7 @@ function main() {
const families = FAMILIES.map((family) => ({
commandAliases: family.commandAliases,
subcommands: family.subcommands,
routerPath: path.join(ROOT, 'gsd-core', 'bin', 'lib', `${family.router}.cjs`),
routerPath: path.join(ROOT, 'msd-core', 'bin', 'lib', `${family.router}.cjs`),
}));
for (const family of families) {

View File

@@ -2,7 +2,7 @@
/**
* check-contract-drift.cjs
*
* Enforces that gsd-core/references/agent-contracts.md's `## Agent Registry`
* Enforces that msd-core/references/agent-contracts.md's `## Agent Registry`
* table stays in sync with reality:
*
* 1. The table itself must parse cleanly (no malformed rows).
@@ -14,7 +14,7 @@
* artifact+query`/`structured-return`, in which case any emitted
* marker is itself a violation (vestigial_marker).
* 4. Every `sentinel-match` row's declared markers must have at least one
* exact-case consumer somewhere under gsd-core/workflows/, commands/,
* exact-case consumer somewhere under msd-core/workflows/, commands/,
* or agents/ (excluding the producing agent's own file).
* 5. Registry roster coverage: every agents/*.md file has exactly one
* row, every row names an agent file that exists
@@ -47,9 +47,9 @@ function resolveRoot(argv) {
}
const ROOT = resolveRoot(process.argv.slice(2));
const CONTRACTS_FILE = path.join(ROOT, 'gsd-core', 'references', 'agent-contracts.md');
const CONTRACTS_FILE = path.join(ROOT, 'msd-core', 'references', 'agent-contracts.md');
const AGENTS_DIR = path.join(ROOT, 'agents');
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
const WORKFLOWS_DIR = path.join(ROOT, 'msd-core', 'workflows');
const COMMANDS_DIR = path.join(ROOT, 'commands');
const {
@@ -92,27 +92,27 @@ function toRepoRelative(absPath) {
/**
* referenceIncludes(content)
*
* Plain scan for `@~/.claude/gsd-core/references/*.md` tokens anywhere in an
* Plain scan for `@~/.claude/msd-core/references/*.md` tokens anywhere in an
* agent file's content -- inside an `<execution_context>` block (already
* covered structurally by `executionContextRefs` in command-contract-helpers,
* but a raw regex over the whole string picks those up too) and, just as
* importantly, OUTSIDE one: agents frequently point at a reference doc from
* plain prose (e.g. "See @~/.claude/gsd-core/references/planner-guidance.md
* plain prose (e.g. "See @~/.claude/msd-core/references/planner-guidance.md
* for ...") rather than from the eager `<execution_context>` include list.
* An agent's completion-marker contract can be authored in such a reference
* file rather than the agent file itself -- gsd-planner declares
* file rather than the agent file itself -- msd-planner declares
* `PLANNING COMPLETE` in its registry row, but the example heading itself
* lives in `gsd-core/references/planner-guidance.md`, which the agent only
* lives in `msd-core/references/planner-guidance.md`, which the agent only
* `@`-includes -- so the producer scan below must follow these includes to
* see markers an agent's contract legitimately delegates to a reference doc.
* Returns ROOT-relative paths (`gsd-core/references/foo.md`), de-duplicated.
* Returns ROOT-relative paths (`msd-core/references/foo.md`), de-duplicated.
*/
function referenceIncludes(content) {
const seen = new Set();
const re = /@~\/\.claude\/gsd-core\/references\/[A-Za-z0-9._-]+\.md/g;
const re = /@~\/\.claude\/msd-core\/references\/[A-Za-z0-9._-]+\.md/g;
let m;
while ((m = re.exec(content)) !== null) {
const relPath = 'gsd-core/references/' + m[0].slice('@~/.claude/gsd-core/references/'.length);
const relPath = 'msd-core/references/' + m[0].slice('@~/.claude/msd-core/references/'.length);
seen.add(relPath);
}
return [...seen];
@@ -199,7 +199,7 @@ function main() {
}
}
// consumerTexts: every *.md under gsd-core/workflows/, commands/, agents/
// consumerTexts: every *.md under msd-core/workflows/, commands/, agents/
// — plus every file-shaped `Consumed by` entry that resolves on disk, so
// a row citing a reference doc or an ADR (e.g. intel-updater's
// docs/adr/22-plan-drift-guard.md) is validated against the real file and
@@ -292,7 +292,7 @@ function main() {
process.stderr.write('\n');
}
process.stderr.write('See gsd-core/references/agent-contracts.md for the registry contract spec.\n\n');
process.stderr.write('See msd-core/references/agent-contracts.md for the registry contract spec.\n\n');
return 1;
}

View File

@@ -19,10 +19,10 @@ const OVERALL_LINES = 70;
const OVERALL_BRANCHES = 60;
const PER_FILE_BRANCHES = 70;
const PER_FILE_FILES = [
'gsd-core/bin/lib/state.cjs',
'gsd-core/bin/lib/phase.cjs',
'gsd-core/bin/lib/verify.cjs',
'gsd-core/bin/lib/init.cjs',
'msd-core/bin/lib/state.cjs',
'msd-core/bin/lib/phase.cjs',
'msd-core/bin/lib/verify.cjs',
'msd-core/bin/lib/init.cjs',
];
const overall = summary.total;

View File

@@ -23,7 +23,7 @@
// npm engines: https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines
// Reproducible builds: https://reproducible-builds.org/docs/source-tree/
// npm ci docs: https://docs.npmjs.com/cli/v10/commands/npm-ci
// gsd-test-runner: https://github.com/open-gsd/gsd-test-runner
// msd-test-runner: https://github.com/open-gsd/gsd-test-runner
const fs = require('fs');
const path = require('path');
@@ -37,9 +37,9 @@ const { describeNpmVersionCheckFailure } = require('./lib/npm-version-check-diag
// is even a node_modules to build with) -- confirmed the hard way, by a
// MODULE_NOT_FOUND crash on every real CI platform after a first attempt at
// this fix routed the npm-version check through the canonical execNpm seam
// (gsd-core/bin/lib/shell-command-projection.cjs), a tsc-compiled artifact
// (msd-core/bin/lib/shell-command-projection.cjs), a tsc-compiled artifact
// that plain does not exist yet at that point in the pipeline. This file
// must stay self-contained: no requires reaching into gsd-core/bin/lib.
// must stay self-contained: no requires reaching into msd-core/bin/lib.
//
// On Windows, npm ships as npm.cmd (a batch wrapper); spawnSync without
// shell:true requires the exact filename including extension.

View File

@@ -37,7 +37,7 @@ const fs = require('node:fs');
const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { tryWithinRoot, PathAcceptance } = require('../gsd-core/bin/lib/security.cjs');
const { tryWithinRoot, PathAcceptance } = require('../msd-core/bin/lib/security.cjs');
const ROOT = path.resolve(__dirname, '..');
const CONTEXT_PATH = path.join(ROOT, 'CONTEXT.md');
@@ -45,7 +45,7 @@ const INSTALL_JS_PATH = path.join(ROOT, 'bin', 'install.js');
/**
* Directory prefixes this gate can verify against the shipped tree. A token
* outside these — most importantly `gsd-core/bin/lib/**`, which is generated
* outside these — most importantly `msd-core/bin/lib/**`, which is generated
* and gitignored — is not a claim this gate can check, so it is skipped
* rather than asserted.
*/
@@ -54,10 +54,10 @@ const TRACKED_PREFIXES = [
'tests/',
'scripts/',
'docs/',
'gsd-core/references/',
'gsd-core/workflows/',
'gsd-core/templates/',
'gsd-core/contexts/',
'msd-core/references/',
'msd-core/workflows/',
'msd-core/templates/',
'msd-core/contexts/',
'.github/',
'eslint-rules/',
];

View File

@@ -206,7 +206,7 @@ async function main(argv = process.argv.slice(2)) {
const headers = {
accept: 'application/vnd.github+json',
'x-github-api-version': '2022-11-28',
'user-agent': 'gsd-core-ci-next-health',
'user-agent': 'msd-core-ci-next-health',
};
if (token) headers.authorization = `Bearer ${token}`;
const response = await fetch(url, { headers, signal: AbortSignal.timeout(10000) });

View File

@@ -136,7 +136,7 @@ function usage() {
' PR_NUMBER the pull request number',
' GITHUB_OUTPUT path to append verdict=/mergeable= step outputs to',
' GITHUB_STEP_SUMMARY path to append a human-readable summary to',
' GSD_PR_MERGEABILITY_BASE_DELAY_MS optional numeric override of the base backoff, for tests',
' MSD_PR_MERGEABILITY_BASE_DELAY_MS optional numeric override of the base backoff, for tests',
].join('\n');
}
@@ -187,7 +187,7 @@ async function main(argv = process.argv.slice(2)) {
const prNumberRaw = process.env.PR_NUMBER;
const prNumber = Number.parseInt(prNumberRaw, 10);
const overrideDelay = Number(process.env.GSD_PR_MERGEABILITY_BASE_DELAY_MS);
const overrideDelay = Number(process.env.MSD_PR_MERGEABILITY_BASE_DELAY_MS);
const baseDelayMs = Number.isFinite(overrideDelay) && overrideDelay >= 0 ? overrideDelay : BASE_DELAY_MS;
const fetchPr = async (number) => {
@@ -195,7 +195,7 @@ async function main(argv = process.argv.slice(2)) {
const headers = {
accept: 'application/vnd.github+json',
'x-github-api-version': '2022-11-28',
'user-agent': 'gsd-core-ci-pr-mergeability',
'user-agent': 'msd-core-ci-pr-mergeability',
};
if (token) headers.authorization = `Bearer ${token}`;
const response = await fetch(url, { headers, signal: AbortSignal.timeout(10000) });

View File

@@ -76,7 +76,7 @@ const { fetchRef, mergeRef } = resolveBaseRefs(process.env, 'main');
function main() {
// Configure git identity (needed for merge commit).
runOrThrow('git', ['config', 'user.email', 'ci@gsd-redux'], 'git config user.email');
runOrThrow('git', ['config', 'user.email', 'ci@msd-redux'], 'git config user.email');
runOrThrow('git', ['config', 'user.name', 'CI Rebase Check'], 'git config user.name');
// Set authenticated remote URL.

View File

@@ -121,7 +121,7 @@ const RULES = [
},
{
name: 'TS runtime sources (ADR-457 build-at-publish)',
// src/*.cts compiles into gsd-core/bin/lib/*.cjs; a source-only edit must
// src/*.cts compiles into msd-core/bin/lib/*.cjs; a source-only edit must
// still trigger the migrated module's tests (otherwise CI silently skips them).
match: path => path.startsWith('src/') || path === 'tsconfig.build.json',
tests: [
@@ -133,7 +133,7 @@ const RULES = [
{
name: 'installer and package layout',
match: path => path.startsWith('bin/') ||
path.startsWith('gsd-core/bin/') ||
path.startsWith('msd-core/bin/') ||
path.includes('install') ||
path.includes('release-tarball-smoke'),
fullMatrix: true,
@@ -160,7 +160,7 @@ const RULES = [
// Every source file the installer EMITS into a runtime layout is captured by
// the emitted-attribution differential + the install-tree snapshot. A source
// edit here that changes emitted output MUST re-verify (#2266: a
// hooks/gsd-statusline.js edit changed installed output but no rule selected
// hooks/msd-statusline.js edit changed installed output but no rule selected
// the drift guard, so a stale emitted state shipped to next undetected).
// Union semantics: this ADDS the drift guard on top of each path's existing
// content-specific tests. Targeted lane only (the real-tree test skips win32
@@ -168,21 +168,21 @@ const RULES = [
// #2724: golden-install-parity.test.cjs is retired (ADR-2719 Phase 4); the
// emitted differential (ADR-2719 Phase 2/3) is now the sole gate for a PR
// editing only shipped content, the archetypal emitted-ripple case.
// NOTE: intentionally NOT a blanket 'gsd-core/' prefix, for two reasons:
// (1) gsd-core/bin/** is tsc-compiled runtime output — EXCLUDED_PREFIXES-
// NOTE: intentionally NOT a blanket 'msd-core/' prefix, for two reasons:
// (1) msd-core/bin/** is tsc-compiled runtime output — EXCLUDED_PREFIXES-
// excluded from both manifests, and already covered by the 'installer and
// package layout' rule (path.startsWith('gsd-core/bin/')) — so matching it
// package layout' rule (path.startsWith('msd-core/bin/')) — so matching it
// here would be pure noise; and
// (2) enumerating only the installer-shipped content subtrees preserves the
// bug-408 unit-fallback contract: a gsd-core/ path that is NOT shipped
// verbatim (the bug-408 test uses gsd-core/src/some-util.js) must still
// bug-408 unit-fallback contract: a msd-core/ path that is NOT shipped
// verbatim (the bug-408 test uses msd-core/src/some-util.js) must still
// fall back to ['unit'] when no rule matches.
// Listed: the four gsd-core content subtrees the installer ships verbatim
// Listed: the four msd-core content subtrees the installer ships verbatim
// (contexts, references, templates, workflows) + bin/shared/*.json data files.
// Verify against Object.keys(golden fixture) grouped by gsd-core/<subdir>.
// Verify against Object.keys(golden fixture) grouped by msd-core/<subdir>.
match: path =>
['hooks/', 'commands/', 'agents/', 'skills/', 'gsd-core/workflows/', 'gsd-core/templates/', 'gsd-core/references/', 'gsd-core/contexts/', 'scripts/changeset/', 'scripts/lib/'].some(p => path.startsWith(p)) ||
(path.startsWith('gsd-core/bin/shared/') && path.endsWith('.json')) ||
['hooks/', 'commands/', 'agents/', 'skills/', 'msd-core/workflows/', 'msd-core/templates/', 'msd-core/references/', 'msd-core/contexts/', 'scripts/changeset/', 'scripts/lib/'].some(p => path.startsWith(p)) ||
(path.startsWith('msd-core/bin/shared/') && path.endsWith('.json')) ||
['scripts/fix-slash-commands.cjs', 'scripts/gen-capability-registry.cjs', 'scripts/gen-loop-host-contract.cjs'].includes(path),
tests: [
'tests/golden-install-tree.test.cjs',
@@ -244,7 +244,7 @@ const RULES = [
},
{
name: 'workflow prompts',
match: path => path.startsWith('gsd-core/workflows/'),
match: path => path.startsWith('msd-core/workflows/'),
tests: [
'tests/workflow-compat.test.cjs',
'tests/workflow-size-budget.test.cjs',
@@ -254,7 +254,7 @@ const RULES = [
// consolidation epic #1969 (B6 #1975) and folded into slash-command-namespace.test.cjs
// ("folded:bug-3683-workflow-colon-namespace-leak" describe block). The stale filename
// here was itself an instance of this issue's defect class — silently dropped by
// existingTests() below, so gsd-core/workflows/ changes stopped re-running this
// existingTests() below, so msd-core/workflows/ changes stopped re-running this
// regression's coverage with nothing signaling it.
'tests/slash-command-namespace.test.cjs',
],
@@ -563,7 +563,7 @@ function classify(files, reachabilityDeps = {}) {
// rule fired in `reasons` but classify()'s codeChanged gate zeroed out every
// targeted test because 'skills/' was absent from this list).
if (
['bin/', 'src/', 'gsd-core/', 'agents/', 'commands/', 'hooks/', 'skills/', 'tests/', 'scripts/', 'eslint-rules/'].some(p => file.startsWith(p)) ||
['bin/', 'src/', 'msd-core/', 'agents/', 'commands/', 'hooks/', 'skills/', 'tests/', 'scripts/', 'eslint-rules/'].some(p => file.startsWith(p)) ||
file === 'package.json' || file === 'package-lock.json' ||
(file.startsWith('tsconfig') && file.endsWith('.json')) ||
file.startsWith('.github/rulesets/')

View File

@@ -2,7 +2,7 @@
/**
* command-contract-helpers.cjs (ADR-0002)
*
* Single source of truth for the commands/gsd/*.md contract constants and
* Single source of truth for the commands/msd/*.md contract constants and
* parsers shared by scripts/lint-command-contract.cjs and
* tests/command-contract.test.cjs.
*
@@ -54,7 +54,7 @@ function executionContextRefs(content) {
const trailingProse = line.length > token.length;
const normalized = token
.replace(/^@(?:~|\$HOME)\//, '')
.replace(/^(?:\.claude\/)?(?:gsd-core\/)?/, '');
.replace(/^(?:\.claude\/)?(?:msd-core\/)?/, '');
refs.push({ token, normalized, trailingProse });
}
}
@@ -64,7 +64,7 @@ function executionContextRefs(content) {
/**
* workflowPathRefs(content)
*
* Locates every gsd-core-relative workflow path referenced in a markdown
* Locates every msd-core-relative workflow path referenced in a markdown
* string, whether the reference is an eager @-include (already covered by
* executionContextRefs) or a *lazy* path mentioned only in prose/code — a
* path a command reads on demand via Read/Bash rather than an @-inclusion
@@ -77,8 +77,8 @@ function executionContextRefs(content) {
*
* A. Any path whose segments include `workflows/`, optionally preceded by
* an eager `@`, a home-dir prefix (`~/` or `$HOME/`), `.claude/`, and/or
* `gsd-core/` — e.g. `@~/.claude/gsd-core/workflows/scan.md`,
* `gsd-core/workflows/x.md`, or a bare `workflows/x.md`.
* `msd-core/` — e.g. `@~/.claude/msd-core/workflows/scan.md`,
* `msd-core/workflows/x.md`, or a bare `workflows/x.md`.
* B. Same as A but without the eager `@` — a lazy reference read on
* demand rather than inlined at load time.
* C. Parent-relative sub-file paths with no `workflows/` prefix at all —
@@ -105,14 +105,14 @@ function workflowPathRefs(content) {
refs.push(normalized);
}
const shapeARe = /@?(?:(?:~|\$HOME)\/)?(?:\.claude\/)?(?:gsd-core\/)?workflows\/[A-Za-z0-9._/-]+\.md(?![A-Za-z0-9_])/g;
const shapeARe = /@?(?:(?:~|\$HOME)\/)?(?:\.claude\/)?(?:msd-core\/)?workflows\/[A-Za-z0-9._/-]+\.md(?![A-Za-z0-9_])/g;
let m;
while ((m = shapeARe.exec(content)) !== null) {
const normalized = m[0]
.replace(/^@/, '')
.replace(/^(?:~|\$HOME)\//, '')
.replace(/^\.claude\//, '')
.replace(/^gsd-core\//, '');
.replace(/^msd-core\//, '');
addRef(normalized);
}
@@ -125,12 +125,12 @@ function workflowPathRefs(content) {
}
/**
* unreachableWorkflows(loaderContents, gsdFiles, workflowPaths)
* unreachableWorkflows(loaderContents, msdFiles, workflowPaths)
*
* Computes reachability over the gsd-core file graph and reports which
* Computes reachability over the msd-core file graph and reports which
* `workflowPaths` are never reached, starting only from `loaderContents`
* (commands/agents/skills — the files a runtime actually loads) and walking
* `workflowPathRefs` edges transitively through `gsdFiles`.
* `workflowPathRefs` edges transitively through `msdFiles`.
*
* The seed set is deliberately restricted to loaders and never includes a
* workflow's own content. Seeding from workflows too would let two failure
@@ -143,7 +143,7 @@ function workflowPathRefs(content) {
* mean "a runtime can actually get here," not merely "something points to
* it."
*
* `gsdFiles` covers all of `gsd-core/**`, not just `workflows/`, because a
* `msdFiles` covers all of `msd-core/**`, not just `workflows/`, because a
* `references/` or `templates/` file can itself name a workflow path and
* needs to be walked through to propagate reachability — restricting the map
* to `workflows/` would silently break any chain that passes through a
@@ -152,7 +152,7 @@ function workflowPathRefs(content) {
* `visited` guards the walk against reference cycles (including the
* mutual/self cases above) so traversal always terminates.
*/
function unreachableWorkflows(loaderContents, gsdFiles, workflowPaths) {
function unreachableWorkflows(loaderContents, msdFiles, workflowPaths) {
const visited = new Set();
const queue = [];
@@ -164,8 +164,8 @@ function unreachableWorkflows(loaderContents, gsdFiles, workflowPaths) {
const p = queue.pop();
if (visited.has(p)) continue;
visited.add(p);
if (gsdFiles.has(p)) {
for (const ref of workflowPathRefs(gsdFiles.get(p))) queue.push(ref);
if (msdFiles.has(p)) {
for (const ref of workflowPathRefs(msdFiles.get(p))) queue.push(ref);
}
}
@@ -351,7 +351,7 @@ function extractMarkers(content, knownMarkers) {
* parseAgentContracts(markdown)
*
* Parses the `## Agent Registry` pipe table in
* gsd-core/references/agent-contracts.md into structured rows, keyed by
* msd-core/references/agent-contracts.md into structured rows, keyed by
* lowercased, underscore-joined column headers (`Completion Markers` ->
* `completion_markers`). Column set is read from the header row itself, so
* the table can grow a `Consumed By` / `Kind` column later without this
@@ -703,7 +703,7 @@ function contractViolations({ registry, producerMarkers, candidateMarkers, consu
* prose — so every backtick span is inspected and only tokens that look like
* repo-relative markdown paths (`^[\w][\w./-]*\.md$` — no spaces, no glob
* stars) are returned. A glob (`*-VERIFICATION.md`) or a command
* (`gsd_run query verification.status`) describes the consumption mechanism
* (`msd_run query verification.status`) describes the consumption mechanism
* in prose; it is not a file the check can open, and is ignored.
*/
function parseConsumedByCell(value) {
@@ -797,8 +797,8 @@ function readTagViolations({ registry, agentTexts, consumerTexts } = {}) {
* silently.
*/
const NON_AGENT_TOKENS = new Set([
// Display headings the /gsd:graphify command itself renders from
// `gsd_run graphify build` CLI output — tool output shown to the user,
// Display headings the /msd:graphify command itself renders from
// `msd_run graphify build` CLI output — tool output shown to the user,
// not an agent return any spawner dispatches on.
'GRAPHIFY BUILD COMPLETE',
'GRAPHIFY BUILD FAILED',
@@ -831,7 +831,7 @@ function unmatchedConsumerTokens({ consumerTexts, vocabulary } = {}) {
const reported = new Set();
for (const [file, text] of consumers.entries()) {
if (!file.startsWith('gsd-core/workflows/') && !file.startsWith('commands/')) continue;
if (!file.startsWith('msd-core/workflows/') && !file.startsWith('commands/')) continue;
if (typeof text !== 'string') continue;
tokenRe.lastIndex = 0;
let m;
@@ -898,7 +898,7 @@ const VIOLATION_KINDS = Object.freeze({
* new kind cannot ship without its remedy.
*/
const REMEDIES = Object.freeze({
parse_error: 'fix the malformed row in gsd-core/references/agent-contracts.md',
parse_error: 'fix the malformed row in msd-core/references/agent-contracts.md',
unclosed_fence: 'close the unterminated code fence in this agent file',
declared_marker_not_emitted:
'remove the marker from the registry row, or emit it as an in-fence example in the agent file',

View File

@@ -306,7 +306,7 @@ const DOCS_GUARD_TESTS = {
'docs/ko-KR/reference/state-md.md',
'docs/pt-BR/reference/state-md.md',
],
'tests/gsd-write-guard.test.cjs': ['docs/USER-GUIDE.md'],
'tests/msd-write-guard.test.cjs': ['docs/USER-GUIDE.md'],
'tests/host-integration-descriptors.test.cjs': [
'docs/reference/host-integration-capability-matrix.md',
],
@@ -316,7 +316,7 @@ const DOCS_GUARD_TESTS = {
'tests/intel.test.cjs': ['*'],
'tests/inventory-headings-countfree.test.cjs': ['docs/INVENTORY.md'],
'tests/inventory-manifest-sync.test.cjs': ['docs/INVENTORY.md', 'docs/INVENTORY-MANIFEST.json'],
'tests/kilo-upgrades.test.cjs': ['docs/how-to/connect-gsd-mcp-server.md'],
'tests/kilo-upgrades.test.cjs': ['docs/how-to/connect-msd-mcp-server.md'],
'tests/live-config-guard.test.cjs': ['docs/TESTING-SUITES.md'],
// #3726: pins the `milestone complete` synopsis (English + four localized
// mirrors), the `--confirm` flag row and the guard-override instructions —

View File

@@ -1,15 +1,15 @@
'use strict';
/**
* One-shot script + library: bidirectional GSD slash-command namespace normalizer.
* One-shot script + library: bidirectional MSD slash-command namespace normalizer.
*
* - Default direction (transformContent): retired /gsd-<cmd> → /gsd:<cmd>
* - Default direction (transformContent): retired /msd-<cmd> → /msd:<cmd>
* (keeps monorepo sources, docs, and workflows in the active colon form).
* - Reverse direction (transformContentToHyphen): /gsd:<cmd> / gsd:<cmd> → gsd-<cmd>
* - Reverse direction (transformContentToHyphen): /msd:<cmd> / msd:<cmd> → msd-<cmd>
* (used during skill installation for runtimes that register skills under the
* canonical hyphen form established in #2808).
*
* Both directions only rewrite known commands from `commands/gsd/*.md` (longest-first
* matching + word-boundary safety). Non-commands (gsd-sdk, gsd-tools, etc.) are
* Both directions only rewrite known commands from `commands/msd/*.md` (longest-first
* matching + word-boundary safety). Non-commands (msd-sdk, msd-tools, etc.) are
* intentionally left untouched.
*
* The transforms are pure and exported for use by the installer and tests.
@@ -18,14 +18,14 @@
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');
const SEARCH_DIRS = [
path.join(__dirname, '..', 'gsd-core', 'bin', 'lib'),
path.join(__dirname, '..', 'gsd-core', 'workflows'),
path.join(__dirname, '..', 'gsd-core', 'references'),
path.join(__dirname, '..', 'gsd-core', 'templates'),
path.join(__dirname, '..', 'gsd-core', 'contexts'),
path.join(__dirname, '..', 'commands', 'gsd'),
path.join(__dirname, '..', 'msd-core', 'bin', 'lib'),
path.join(__dirname, '..', 'msd-core', 'workflows'),
path.join(__dirname, '..', 'msd-core', 'references'),
path.join(__dirname, '..', 'msd-core', 'templates'),
path.join(__dirname, '..', 'msd-core', 'contexts'),
path.join(__dirname, '..', 'commands', 'msd'),
path.join(__dirname, '..', 'agents'),
path.join(__dirname, '..', 'hooks'),
];
@@ -44,30 +44,30 @@ function isTestFile(name) {
}
function buildPattern(cmdNames) {
// Empty input would compile `/gsd-()(?=[^a-zA-Z0-9_-]|$)/g`, which the regex
// engine still matches at any `/gsd-` token followed by a non-word boundary
// (e.g. EOL, whitespace, punctuation) — rewriting it to a stray `/gsd:`.
// Empty input would compile `/msd-()(?=[^a-zA-Z0-9_-]|$)/g`, which the regex
// engine still matches at any `/msd-` token followed by a non-word boundary
// (e.g. EOL, whitespace, punctuation) — rewriting it to a stray `/msd:`.
// Short-circuit so the caller can no-op on a missing/empty registry rather
// than perform an unintended broad rewrite.
if (!Array.isArray(cmdNames) || cmdNames.length === 0) return null;
const sorted = [...cmdNames].sort((a, b) => b.length - a.length); // longest first to avoid partial matches
return new RegExp(`/gsd-(${sorted.join('|')})(?=[^a-zA-Z0-9_-]|$)`, 'g');
return new RegExp(`/msd-(${sorted.join('|')})(?=[^a-zA-Z0-9_-]|$)`, 'g');
}
/**
* Pure transform: rewrite retired `/gsd-<cmd>` to `/gsd:<cmd>` for the given command names.
* Returns the rewritten string. Identifiers not in `cmdNames` (e.g. `/gsd-sdk`,
* `/gsd-tools`) are left untouched.
* Pure transform: rewrite retired `/msd-<cmd>` to `/msd:<cmd>` for the given command names.
* Returns the rewritten string. Identifiers not in `cmdNames` (e.g. `/msd-sdk`,
* `/msd-tools`) are left untouched.
*/
function transformContent(src, cmdNames) {
const pattern = buildPattern(cmdNames);
if (!pattern) return src;
return src.replace(pattern, (_, cmd) => `/gsd:${cmd}`);
return src.replace(pattern, (_, cmd) => `/msd:${cmd}`);
}
/**
* Build regex for the reverse direction (colon form → hyphen form).
* Matches both "gsd:cmd" and "/gsd:cmd" (the leading / is preserved automatically
* Matches both "msd:cmd" and "/msd:cmd" (the leading / is preserved automatically
* because it is not part of the match). Uses longest-first ordering plus
* bidirectional word-boundary safety (negative lookbehind on the left, lookahead
* on the right) so matches only occur at token boundaries.
@@ -75,20 +75,20 @@ function transformContent(src, cmdNames) {
function buildColonPattern(cmdNames) {
if (!Array.isArray(cmdNames) || cmdNames.length === 0) return null;
const sorted = [...cmdNames].sort((a, b) => b.length - a.length);
return new RegExp(`(?<![a-zA-Z0-9_-])gsd:(${sorted.join('|')})(?=[^a-zA-Z0-9_-]|$)`, 'g');
return new RegExp(`(?<![a-zA-Z0-9_-])msd:(${sorted.join('|')})(?=[^a-zA-Z0-9_-]|$)`, 'g');
}
/**
* Pure transform (reverse): rewrite `/gsd:<cmd>` / `gsd:<cmd>` to hyphen form
* for known GSD commands.
* Pure transform (reverse): rewrite `/msd:<cmd>` / `msd:<cmd>` to hyphen form
* for known MSD commands.
*
* Non-command identifiers (e.g. gsd-sdk, gsd-tools) are left untouched, matching
* Non-command identifiers (e.g. msd-sdk, msd-tools) are left untouched, matching
* the safety contract of the forward transform.
*/
function transformContentToHyphen(src, cmdNames) {
const pattern = buildColonPattern(cmdNames);
if (!pattern) return src;
return src.replace(pattern, (_, cmd) => `gsd-${cmd}`);
return src.replace(pattern, (_, cmd) => `msd-${cmd}`);
}
function readCmdNames() {
@@ -102,7 +102,7 @@ function readCmdNames() {
// not silently handed an empty registry while the real problem goes undetected.
if (err.code !== 'ENOENT') throw err;
// COMMANDS_DIR may not exist on installs that use skill-based runtimes or
// global Claude installs (no local commands/gsd/ directory). Return [] so
// global Claude installs (no local commands/msd/ directory). Return [] so
// callers that handle an empty array gracefully (buildPattern returns null,
// transformContent is a no-op) are not broken by a missing directory.
return [];

View File

@@ -30,8 +30,8 @@ const fs = require('node:fs');
const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { escapeRegex: escapeRegExp } = require('../gsd-core/bin/lib/pattern.cjs');
const { isContainedIn } = require('../gsd-core/bin/lib/security.cjs');
const { escapeRegex: escapeRegExp } = require('../msd-core/bin/lib/pattern.cjs');
const { isContainedIn } = require('../msd-core/bin/lib/security.cjs');
const ROOT = path.resolve(__dirname, '..');
const ADR_DIR = path.join(ROOT, 'docs', 'adr');

View File

@@ -5,14 +5,14 @@
* gen-capability-matrix.cjs — ADR-1244 Phase 6 (Decision D9).
*
* Generates docs/reference/capability-matrix.md FROM the committed capability
* registry (gsd-core/bin/lib/capability-registry.cjs), so the matrix can never
* registry (msd-core/bin/lib/capability-registry.cjs), so the matrix can never
* drift from the actual capability set. Kept honest by a drift guard
* (tests/capability-matrix-sync.test.cjs runs `--check`).
*
* The matrix is RELEASE-STABLE by design: it does NOT embed each capability's
* exact `version` (which tracks the GSD package version in lockstep and would
* exact `version` (which tracks the MSD package version in lockstep and would
* churn the committed file — and trip the drift guard — on every release). It
* shows `engines.gsd` (the stable host-compatibility RANGE) instead, and notes
* shows `engines.msd` (the stable host-compatibility RANGE) instead, and notes
* the version-lockstep rule in prose. The committed matrix therefore changes
* only on intentional capability edits (add/remove a capability, change its
* tier/role/engines/extension-points/hook-kinds) — never on a version bump.
@@ -28,7 +28,7 @@ const path = require('path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.resolve(__dirname, '..');
const REGISTRY_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'capability-registry.cjs');
const REGISTRY_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'capability-registry.cjs');
const MATRIX_PATH = path.join(ROOT, 'docs', 'reference', 'capability-matrix.md');
/** Canonical loop extension points, in order (mirrors the phase loop). */
@@ -89,7 +89,7 @@ function fmtKinds(set) {
}
function fmtEngines(cap) {
const g = cap && cap.engines && cap.engines.gsd;
const g = cap && cap.engines && cap.engines.msd;
return typeof g === 'string' && g ? '`' + g + '`' : '—';
}
@@ -103,7 +103,7 @@ function renderTable(caps, role, extByCap) {
return `| \`${c.id}\` | ${c.role} | ${c.tier || '—'} | ${fmtEngines(c)} | ${fmtPoints(ext.points)} | ${fmtKinds(ext.kinds)} | first-party |`;
});
return [
'| id | role | tier | engines.gsd | extension points | hook kinds | source |',
'| id | role | tier | engines.msd | extension points | hook kinds | source |',
'|---|---|---|---|---|---|---|',
...rows,
].join('\n');
@@ -144,28 +144,28 @@ See also: [ADR-1244](../adr/1244-capability-ecosystem.md) —
| Column | Description |
|---|---|
| **id** | Canonical capability identifier; unique across first- and third-party capabilities. Reserved prefixes: \`gsd-\`, \`gsd-core-\`, \`anthropic-\`. |
| **role** | \`feature\` — extends what the loop does; \`runtime\` — adapts GSD to a specific AI runtime/IDE; \`reviewer\` — declares a cross-AI reviewer lane (ADR-2782). A capability may be both a runtime and a reviewer. |
| **id** | Canonical capability identifier; unique across first- and third-party capabilities. Reserved prefixes: \`msd-\`, \`msd-core-\`, \`anthropic-\`. |
| **role** | \`feature\` — extends what the loop does; \`runtime\` — adapts MSD to a specific AI runtime/IDE; \`reviewer\` — declares a cross-AI reviewer lane (ADR-2782). A capability may be both a runtime and a reviewer. |
| **tier** | \`core\` — always active; \`standard\` — active when the runtime supports it; \`full\` — opt-in or runtime-specific. |
| **engines.gsd** | Semver RANGE expressing host-version compatibility. A hard gate at install and at load. \`—\` means the capability declares no range. |
| **engines.msd** | Semver RANGE expressing host-version compatibility. A hard gate at install and at load. \`—\` means the capability declares no range. |
| **extension points** | The loop points this capability registers hooks into (from the registry's \`byLoopPoint\` index). \`—\` means it registers none (typical for runtime capabilities, whose job is surface emission). |
| **hook kinds** | Which of \`step\`, \`contribution\`, \`gate\` the capability's hooks use. \`—\` means none. |
| **source** | \`first-party\` — ships with GSD Core; \`third-party\` — installed from an external source via \`gsd capability install\`. |
| **source** | \`first-party\` — ships with MSD Core; \`third-party\` — installed from an external source via \`msd capability install\`. |
> **On versions.** This matrix intentionally omits a per-capability \`version\`
> column. First-party capabilities are versioned **in lockstep** with the GSD
> Core package (their \`capability.json\` \`version\` always equals the GSD release
> column. First-party capabilities are versioned **in lockstep** with the MSD
> Core package (their \`capability.json\` \`version\` always equals the MSD release
> version), so a per-row version would simply repeat the package version and
> churn the committed file on every release. The stable host-compatibility
> signal — \`engines.gsd\` — is shown instead. A third-party capability's exact
> version is recorded in the per-runtime ledger (\`.gsd-capabilities.json\`) at
> signal — \`engines.msd\` — is shown instead. A third-party capability's exact
> version is recorded in the per-runtime ledger (\`.msd-capabilities.json\`) at
> install time.
---
## Native (first-party) capabilities
First-party capabilities are implicitly trusted: they ship as part of the GSD
First-party capabilities are implicitly trusted: they ship as part of the MSD
Core package and are stamped with the package version at release (per
ADR-1244 D6). They are not subject to the consent or integrity-pin flow applied
to third-party capabilities.
@@ -180,7 +180,7 @@ ${featureTable}
### Runtime capabilities (role: runtime) — ${runtimeCount}
Runtime capabilities adapt GSD to a specific AI runtime or IDE — emitting
Runtime capabilities adapt MSD to a specific AI runtime or IDE — emitting
skills, agents, hooks configuration, and surface files for that host. They
typically register no loop hooks (their primary responsibility is surface
emission), so their extension-point and hook-kind cells are \`—\`.
@@ -190,12 +190,12 @@ ${runtimeTable}
### Reviewer capabilities (role: reviewer) — ${reviewerCount}
Reviewer capabilities declare a cross-AI **reviewer lane** — one external CLI or
model endpoint \`/gsd-review\` hands a plan to (ADR-2782 D3). They are not install
model endpoint \`/msd-review\` hands a plan to (ADR-2782 D3). They are not install
targets: they emit no skills, agents, hooks or surface files, so their
extension-point and hook-kind cells are \`—\`. A host that is *also* a reviewer
(Claude, Codex, Cursor, OpenCode, Qwen, Antigravity) keeps one manifest and
appears under **runtime** above, carrying its lane alongside its runtime body;
only lanes that GSD never installs into appear here.
only lanes that MSD never installs into appear here.
Because a lane receives the plan text, requirements, research findings and
\`CONTEXT.md\` decisions, it is a disclosed executable surface and is consent-gated
@@ -209,12 +209,12 @@ ${reviewerTable}
## Third-party capabilities
This matrix is the **first-party catalogue**: it is generated from the committed
registry and therefore lists only the capabilities that ship with GSD Core.
registry and therefore lists only the capabilities that ship with MSD Core.
Installed third-party capabilities are NOT written into this committed file. Once a
user installs one via \`gsd capability install <spec>\` it enters the **runtime
user installs one via \`msd capability install <spec>\` it enters the **runtime
registry overlay** (ADR-1244 D2); the overlay-aware view of what is installed on a
given machine is \`gsd capability list\` (see the
[\`gsd capability\` command reference](gsd-capability-command.md)), which reports
given machine is \`msd capability list\` (see the
[\`msd capability\` command reference](msd-capability-command.md)), which reports
first-party and installed third-party capabilities together using the same column
fields described below, with \`source\` = \`third-party\`.
@@ -222,17 +222,17 @@ fields described below, with \`source\` = \`third-party\`.
| Column | Value |
|---|---|
| **id** | As declared in \`capability.json\`. Must not use reserved prefixes (\`gsd-\`, \`gsd-core-\`, \`anthropic-\`). |
| **id** | As declared in \`capability.json\`. Must not use reserved prefixes (\`msd-\`, \`msd-core-\`, \`anthropic-\`). |
| **role** | \`feature\`, \`runtime\`, or \`reviewer\`, as declared. |
| **tier** | \`core\`, \`standard\`, or \`full\`, as declared. |
| **engines.gsd** | Range from \`capability.json\`; verified at install and at each load. |
| **engines.msd** | Range from \`capability.json\`; verified at install and at each load. |
| **extension points** | The loop points the capability registers into, validated against the known 12 identifiers. |
| **hook kinds** | \`step\`, \`contribution\`, and/or \`gate\` as declared. Disclosed in the consent summary at install. |
| **source** | \`third-party\` |
### Community registry
Whether GSD operates or advertises a central community registry of third-party
Whether MSD operates or advertises a central community registry of third-party
capabilities is **TBD/TBA** (PRD). The matrix mechanic and all manifest fields
ship regardless of that decision; URL/git/npm/tarball import does not depend on
a central registry.
@@ -249,8 +249,8 @@ and the [capability manifest reference](capability-manifest.md).
| Field | Required | Type | Purpose |
|---|---|---|---|
| \`version\` | **Yes** | semver string | Capability version. The registry rejects manifests without it. |
| \`engines.gsd\` | Recommended | semver range | Host-version compatibility gate. Enforced at install and load. |
| \`compatVersions\` | No | object: cap-version → gsd-range | Graceful-downgrade table for sources that enumerate versions (git tags, registry, npm). |
| \`engines.msd\` | Recommended | semver range | Host-version compatibility gate. Enforced at install and load. |
| \`compatVersions\` | No | object: cap-version → msd-range | Graceful-downgrade table for sources that enumerate versions (git tags, registry, npm). |
| \`integrity\` | No | \`sha512-<base64>\` | SHA-512 digest of the fetched bundle. Verified before extraction when present; mismatch aborts. |
| \`provenance\` | No | \`{ sourceRepo, commit }\` | Source provenance; populated in CI for first-party/curated capabilities. |

View File

@@ -2,7 +2,7 @@
'use strict';
/**
* gen-capability-registry.cjs — generates gsd-core/bin/lib/capability-registry.cjs
* gen-capability-registry.cjs — generates msd-core/bin/lib/capability-registry.cjs
* from every capabilities/<id>/capability.json declaration.
*
* Usage:
@@ -19,25 +19,25 @@ const fs = require('node:fs');
const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { normalizeEol } = require('../gsd-core/bin/lib/text-lines.cjs');
const { normalizeEol } = require('../msd-core/bin/lib/text-lines.cjs');
const ROOT = path.resolve(__dirname, '..');
const CAPABILITIES_DIR = path.join(ROOT, 'capabilities');
const REGISTRY_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'capability-registry.cjs');
const CONFIG_SCHEMA_PATH = path.join(ROOT, 'gsd-core', 'bin', 'shared', 'config-schema.manifest.json');
const REGISTRY_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'capability-registry.cjs');
const CONFIG_SCHEMA_PATH = path.join(ROOT, 'msd-core', 'bin', 'shared', 'config-schema.manifest.json');
// ─── Loop Host Contract ───────────────────────────────────────────────────────
//
// Generated from workflow markers by scripts/gen-loop-host-contract.cjs (ADR-894 §3).
// Require the committed gsd-core/bin/lib/loop-host-contract.cjs artifact so the
// Require the committed msd-core/bin/lib/loop-host-contract.cjs artifact so the
// registry generator and the loop-host-contract generator share one source of truth.
const { LOOP_HOST_CONTRACT } = require('../gsd-core/bin/lib/loop-host-contract.cjs');
const { LOOP_HOST_CONTRACT } = require('../msd-core/bin/lib/loop-host-contract.cjs');
// Wired-kinds helper — per point, which hook kinds the render-hooks call sites' dispatch text covers.
const { getWiredKinds } = require('./gen-loop-host-contract.cjs');
// Capability validator — shared runtime-callable module extracted per ADR-1244 D2.
const capValidator = require('../gsd-core/bin/lib/capability-validator.cjs');
const capValidator = require('../msd-core/bin/lib/capability-validator.cjs');
// Destructure only what the generator's own function bodies reference directly.
// Everything else is re-exported from capValidator in module.exports below.
const {
@@ -199,10 +199,10 @@ function loadCentralConfigPatterns(schemaPath = CONFIG_SCHEMA_PATH) {
// (Config-slice validation, per-capability validators, contract validators,
// cross-capability validators, topo-sort helpers, and classifyCrossErrors have
// been moved to gsd-core/bin/lib/capability-validator.cjs per ADR-1244 D2.)
// been moved to msd-core/bin/lib/capability-validator.cjs per ADR-1244 D2.)
const INSTALL_PROFILES_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'install-profiles.cjs');
const CLUSTERS_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'clusters.cjs');
const INSTALL_PROFILES_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'install-profiles.cjs');
const CLUSTERS_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'clusters.cjs');
let _installProfilesMod = null;
let _clustersMod = null;
@@ -338,8 +338,8 @@ function runConsistencyGate(capabilityClusters, profileMembership, capMap) {
// FIX 1: load the REAL skills manifest once (same path as bin/install.js uses),
// so resolveProfile expands requires:-closure and the effective set is accurate.
const commandsGsdDir = path.join(ROOT, 'commands', 'gsd');
const skillsManifest = loadSkillsManifest(commandsGsdDir);
const commandsMsdDir = path.join(ROOT, 'commands', 'msd');
const skillsManifest = loadSkillsManifest(commandsMsdDir);
// FIX 1: resolve each profile's effective set once and cache — don't reload per-capability.
const profileEffectiveSetCache = Object.create(null);
@@ -842,7 +842,7 @@ function main() {
if (!fs.existsSync(REGISTRY_PATH)) {
process.stderr.write(
'gsd-core/bin/lib/capability-registry.cjs does not exist. Run:\n' +
'msd-core/bin/lib/capability-registry.cjs does not exist. Run:\n' +
' node scripts/gen-capability-registry.cjs --write\n',
);
throw new ExitError(1);
@@ -851,13 +851,13 @@ function main() {
const committed = fs.readFileSync(REGISTRY_PATH, 'utf8');
if (normalizeEol(stripGeneratedComment(committed)) !== normalizeEol(stripGeneratedComment(live))) {
process.stderr.write(
'gsd-core/bin/lib/capability-registry.cjs is stale. Run:\n' +
'msd-core/bin/lib/capability-registry.cjs is stale. Run:\n' +
' node scripts/gen-capability-registry.cjs --write\n',
);
throw new ExitError(1);
}
process.stdout.write('gsd-core/bin/lib/capability-registry.cjs is up to date.\n');
process.stdout.write('msd-core/bin/lib/capability-registry.cjs is up to date.\n');
} else if (flag === '--write') {
// Fix #3: read the REAL central config keys so collision detection fires and is visible.
const centralKeys = loadCentralConfigKeys();

View File

@@ -22,7 +22,7 @@
* The generated artifact is plain JSON (docs/CONTEXT-INDEX.json), mirroring
* docs/INVENTORY-MANIFEST.json's precedent: a committed, generated,
* `--check`-guarded JSON manifest that is NOT runtime code. It previously
* lived at gsd-core/bin/lib/context-index.cjs — a shipped runtime module is
* lived at msd-core/bin/lib/context-index.cjs — a shipped runtime module is
* the wrong place for ~120 KB of arbitrary CONTEXT.md prose: it tripped both
* tests/cline-install.test.cjs's leaked-`.claude`-path guard and
* tests/package-name-single-source.test.cjs's hardcoded-package-name guard,
@@ -30,7 +30,7 @@
* artifact to docs/ (never scanned as runtime code) fixes both without
* weakening either guard.
*
* Depends on the COMPILED gsd-core/bin/lib/context-predicates.cjs
* Depends on the COMPILED msd-core/bin/lib/context-predicates.cjs
* (src/context-predicates.cts, built by `npm run build:lib`). This is safe
* for CI: `.github/workflows/test.yml` runs `build:lib` before `lint:ci`.
*
@@ -46,10 +46,10 @@ const fs = require('node:fs');
const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { normalizeEol } = require('../gsd-core/bin/lib/text-lines.cjs');
const { normalizeEol } = require('../msd-core/bin/lib/text-lines.cjs');
const ROOT = path.resolve(__dirname, '..');
const CONTEXT_PREDICATES_LIB_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'context-predicates.cjs');
const CONTEXT_PREDICATES_LIB_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'context-predicates.cjs');
const CONTEXT_PATH = path.join(ROOT, 'CONTEXT.md');
const INDEX_PATH = path.join(ROOT, 'docs', 'CONTEXT-INDEX.json');
@@ -134,7 +134,7 @@ function buildFreshIndex(contextPath = CONTEXT_PATH) {
* (docs/CONTEXT-INDEX.json). Plain JSON — not a CommonJS module — because
* this is a generated data manifest (mirroring docs/INVENTORY-MANIFEST.json),
* not runtime code: it must never be `require()`-able from a shipped
* gsd-core/bin/lib/*.cjs module, which is exactly the mistake that leaked
* msd-core/bin/lib/*.cjs module, which is exactly the mistake that leaked
* ~120 KB of CONTEXT.md prose (including `.claude/hooks/...` path literals
* and hardcoded package-name strings) into runtime-code content scanning.
*

View File

@@ -18,14 +18,14 @@
* Builds the emitted manifest set (19 real installer spawns) and the workflow/agent size
* maps for the tree at `--dir <path>` (default: THIS script's own checkout — i.e.
* whatever commit is currently checked out where `gen-emitted-baseline.cjs` itself
* lives). Writes the result to `--out <path>` (default `.gsd-cache/emitted-baseline.json`).
* lives). Writes the result to `--out <path>` (default `.msd-cache/emitted-baseline.json`).
*
* `--dir` decouples "which copy of this script runs" from "which tree gets measured"
* (#2767). The MEASUREMENT SCHEMA — this script, and the `currentManifests`/
* `currentSizes`/`buildParityManifest` functions it calls — always comes from wherever
* `gen-emitted-baseline.cjs` itself is being run from (relative `require`s resolve
* there); only the tree being measured (which `bin/install.js` gets spawned, which
* `hooks/`/`gsd-core/workflows/`/`agents/` get read) moves to `--dir`. That is what lets
* `hooks/`/`msd-core/workflows/`/`agents/` get read) moves to `--dir`. That is what lets
* a differential apply ONE definition of "the emitted manifest" to two different
* commits and stay comparable even as that definition evolves — see
* `tests/helpers/emitted-runtime.cjs`'s `buildBaselineAtRef` for the caller that
@@ -36,7 +36,7 @@
* ## Callers
*
* 1. CI's push-to-`next` job runs this straight after `next` advances (no `--dir`,
* measuring its own checkout), then uploads `.gsd-cache/emitted-baseline.json` as a
* measuring its own checkout), then uploads `.msd-cache/emitted-baseline.json` as a
* cache entry keyed on the merge sha (.github/workflows/test.yml,
* `publish-emitted-baseline` job).
* 2. `tests/emitted-attribution.test.cjs`'s real-tree test passes
@@ -45,7 +45,7 @@
* `git worktree`, symlinks in `node_modules` and runs `npm run build:lib` there
* (this script and the test helpers are Node-builtins-only per CONTRIBUTING.md's
* "No external dependencies in core", but `tests/helpers/install-shared.cjs`
* requires the TSC-compiled, gitignored `gsd-core/bin/lib/*.cjs`, so that one build
* requires the TSC-compiled, gitignored `msd-core/bin/lib/*.cjs`, so that one build
* step is unavoidable), then spawns THIS repo's OWN `gen-emitted-baseline.cjs`
* with `--dir <worktree> --out <tmp>` and reads the artifact back.
*
@@ -63,7 +63,7 @@ const { currentManifests, currentSizes, git } = require('../tests/helpers/emitte
const { BASELINE_VERSION } = require('../tests/helpers/emitted-baseline.cjs');
const REPO_ROOT = path.join(__dirname, '..');
const DEFAULT_OUT = path.join(REPO_ROOT, '.gsd-cache', 'emitted-baseline.json');
const DEFAULT_OUT = path.join(REPO_ROOT, '.msd-cache', 'emitted-baseline.json');
// Reuses emitted-runtime.cjs's `git()` (not a local execFileSync) so the
// `-c safe.directory=<dir>` fix for the remote runner's dubious-ownership

View File

@@ -6,7 +6,7 @@
* code-registry epic.
*
* Generates docs/reference/exit-codes.md FROM the exit-code declaration
* (gsd-core/bin/shared/exit-codes.json), so the human-facing reference page
* (msd-core/bin/shared/exit-codes.json), so the human-facing reference page
* can never drift from the actual registered codes — the same allocator-less
* failure mode this epic exists to close, now closed for the doc surface too.
* Modelled directly on scripts/gen-capability-matrix.cjs ->
@@ -15,7 +15,7 @@
* byte-compare drift check.
*
* The declaration JSON is read directly (not the compiled
* gsd-core/bin/lib/exit-code-registry.cjs artifact, which is gitignored tsc
* msd-core/bin/lib/exit-code-registry.cjs artifact, which is gitignored tsc
* output) so this generator — like scripts/gen-exit-code-registry.cjs itself —
* works on an unbuilt clone. The "Reserved bands" table below is DERIVED,
* not retyped: its ranges and allocatable/reserved status come from
@@ -61,15 +61,15 @@ const BAND_SCAN_MAX = 500;
const BAND_PROSE = Object.freeze({
free: '**Free — never allocatable.** `0` is the universal "succeeded" convention and `1` is the universal "failed, no further detail" convention across nearly every CLI ecosystem. Registering either here would collide with that universal meaning instead of adding a distinct, named signal — so the registry leaves both permanently unallocated.',
'hook-only': 'Reserved exclusively to the Claude Code hook-protocol deny (`HOOK_DENY`) — owned by `hook-adapter` and no other module.',
'node-reserved': '**Node-reserved.** Node.js itself assigns meaning to this range (e.g. internal JavaScript errors, fatal exceptions, invalid argument errors) before a GSD process ever gets a chance to project its own outcome. Allocating one of these would be indistinguishable from a Node-level failure the process never intended to report.',
'outside-every-band': 'Outside every allocatable band — not Node-reserved, but also not opened for GSD use. `126`+ additionally collides with the shell convention for "command not executable" / "signal N" (`128+N`), which a process exit code must never impersonate.',
'node-reserved': '**Node-reserved.** Node.js itself assigns meaning to this range (e.g. internal JavaScript errors, fatal exceptions, invalid argument errors) before a MSD process ever gets a chance to project its own outcome. Allocating one of these would be indistinguishable from a Node-level failure the process never intended to report.',
'outside-every-band': 'Outside every allocatable band — not Node-reserved, but also not opened for MSD use. `126`+ additionally collides with the shell convention for "command not executable" / "signal N" (`128+N`), which a process exit code must never impersonate.',
generic: '**Generic band.** Codes any module may use for caller-facing, non-domain-specific outcomes (bad argv, no input in scope, a missing prerequisite, an internal crash).',
domain: '**Domain band.** Codes reserved for a specific product surface\'s own vocabulary — currently only `gsd-tools`\' `DEGRADED` (a completed run reporting a condition through its payload rather than as a process failure).',
domain: '**Domain band.** Codes reserved for a specific product surface\'s own vocabulary — currently only `msd-tools`\' `DEGRADED` (a completed run reporting a condition through its payload rather than as a process failure).',
});
/**
* The 'shell-signal' category (126+) is folded into the SAME rendered row
* as 'outside-every-band' (both read "not opened for GSD use" to a reader —
* as 'outside-every-band' (both read "not opened for MSD use" to a reader —
* the original hand-authored table merged them into one row, and the prose
* above documents the 126+ collision inline). Every other category gets its
* own row, in this fixed display order.
@@ -202,7 +202,7 @@ function buildDoc(entries) {
> **Generated file — do not edit by hand.**
> This page is generated from the exit-code declaration
> (\`gsd-core/bin/shared/exit-codes.json\`) by \`scripts/gen-exit-code-docs.cjs\`
> (\`msd-core/bin/shared/exit-codes.json\`) by \`scripts/gen-exit-code-docs.cjs\`
> and kept honest by a drift guard in \`npm run lint:generated-sync\` (which runs
> \`node scripts/gen-exit-code-docs.cjs --check\`). Any manual edit is overwritten
> on the next generation run. To register a new code, add an entry to the
@@ -217,7 +217,7 @@ See also: [ADR-3889 — one exit-code registry](../adr/3889-process-exit-contrac
## Registered codes (${registeredCount})
Every process-level exit code \`gsd-tools\`, its hooks, and its scripts may terminate
Every process-level exit code \`msd-tools\`, its hooks, and its scripts may terminate
with, by name, meaning, and the module that owns it.
${registeredTable}

View File

@@ -1,11 +1,11 @@
#!/usr/bin/env node
/**
* gen-exit-code-registry.cjs — generates FIVE byte-identical/derived
* artifacts from the declaration at gsd-core/bin/shared/exit-codes.json:
* - gsd-core/bin/lib/exit-code-registry.cjs (tsc-adjacent build tree)
* artifacts from the declaration at msd-core/bin/shared/exit-codes.json:
* - msd-core/bin/lib/exit-code-registry.cjs (tsc-adjacent build tree)
* - scripts/lib/exit-code-registry.cjs (committed, for scripts/
* consumers that must work on an unbuilt clone — same reason
* scripts/lib/cli-exit.cjs exists alongside gsd-core/bin/lib/cli-exit.cjs;
* scripts/lib/cli-exit.cjs exists alongside msd-core/bin/lib/cli-exit.cjs;
* see scripts/gen-scripts-cli-exit.cjs).
* - hooks/lib/exit-code-registry.js (committed, for hooks/
* consumers that must work on a raw, unbuilt clone — same reason as the
@@ -18,7 +18,7 @@
* against the shape the .cjs artifacts above actually export — generated
* from the SAME ENTRY_FIELD_TYPES table serializeRegistry() uses, so the
* two can never independently drift).
* - gsd-core/bin/shared/exit-codes.sh (POSIX sh, safe under
* - msd-core/bin/shared/exit-codes.sh (POSIX sh, safe under
* `set -u`: one `export EXIT_<NAME>=<code>` per entry, sourced by the
* bash scanners under scripts/ so a shell caller never re-invents a
* literal exit-code integer — ADR-3889 Phase 4, #3908).
@@ -57,12 +57,12 @@ const fs = require('node:fs');
const path = require('node:path');
const REPO_ROOT = path.resolve(__dirname, '..');
const DEFAULT_DECLARATION_PATH = path.join(REPO_ROOT, 'gsd-core', 'bin', 'shared', 'exit-codes.json');
const DEFAULT_OUTPUT_PATH = path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'exit-code-registry.cjs');
const DEFAULT_DECLARATION_PATH = path.join(REPO_ROOT, 'msd-core', 'bin', 'shared', 'exit-codes.json');
const DEFAULT_OUTPUT_PATH = path.join(REPO_ROOT, 'msd-core', 'bin', 'lib', 'exit-code-registry.cjs');
const DEFAULT_SCRIPTS_OUTPUT_PATH = path.join(REPO_ROOT, 'scripts', 'lib', 'exit-code-registry.cjs');
const DEFAULT_HOOKS_OUTPUT_PATH = path.join(REPO_ROOT, 'hooks', 'lib', 'exit-code-registry.js');
const DEFAULT_DTS_OUTPUT_PATH = path.join(REPO_ROOT, 'src', 'exit-code-registry.d.cts');
const DEFAULT_SH_OUTPUT_PATH = path.join(REPO_ROOT, 'gsd-core', 'bin', 'shared', 'exit-codes.sh');
const DEFAULT_SH_OUTPUT_PATH = path.join(REPO_ROOT, 'msd-core', 'bin', 'shared', 'exit-codes.sh');
/**
* Single source of the entry field list (name -> TS type), in emission order.
@@ -103,12 +103,12 @@ const USAGE_MESSAGE = [
' (no flag) same as --write',
' --write write all five generated registry artifacts',
' --check exit 1 if ANY committed artifact is stale',
' --declaration override the declaration path (default: gsd-core/bin/shared/exit-codes.json)',
' --out override the primary output artifact path (default: gsd-core/bin/lib/exit-code-registry.cjs)',
' --declaration override the declaration path (default: msd-core/bin/shared/exit-codes.json)',
' --out override the primary output artifact path (default: msd-core/bin/lib/exit-code-registry.cjs)',
' --scripts-out override the secondary output artifact path (default: scripts/lib/exit-code-registry.cjs)',
' --hooks-out override the hooks output artifact path (default: hooks/lib/exit-code-registry.js)',
' --dts-out override the ambient type declaration path (default: src/exit-code-registry.d.cts)',
' --sh-out override the shell-sourceable fragment path (default: gsd-core/bin/shared/exit-codes.sh)',
' --sh-out override the shell-sourceable fragment path (default: msd-core/bin/shared/exit-codes.sh)',
' --json emit ONE JSON report ({ok, reason, context, detail?}) on stdout instead of human-readable prose',
].join('\n');
@@ -378,7 +378,7 @@ function loadDeclaration(declarationPath) {
/**
* Hand-serialize the generated registry module (string concatenation, like
* gsd-core/bin/lib/capability-registry.cjs — no build step, no template
* msd-core/bin/lib/capability-registry.cjs — no build step, no template
* engine, so the emitted bytes are exactly what `--check` re-derives).
*/
function serializeRegistry(entries, declarationPath) {
@@ -389,7 +389,7 @@ function serializeRegistry(entries, declarationPath) {
'// GENERATED FILE — DO NOT EDIT BY HAND.',
`// Source of truth: ${relDeclaration}. Regenerate with:`,
'// node scripts/gen-exit-code-registry.cjs --write',
'// This exact content is emitted to THREE locations — gsd-core/bin/lib/exit-code-registry.cjs,',
'// This exact content is emitted to THREE locations — msd-core/bin/lib/exit-code-registry.cjs,',
'// scripts/lib/exit-code-registry.cjs, and hooks/lib/exit-code-registry.js (the latter two',
'// committed so scripts/ and hooks/ consumers work on an unbuilt clone) — all byte-compared by',
'// `npm run lint:generated-sync` (#3905 ADR-3889 Phase 1; #3906 Phase 2 added the second copy;',
@@ -480,7 +480,7 @@ function serializeDts(declarationPath) {
'//',
'// Ambient type declaration for exit-code-registry.cjs — a GENERATED,',
'// committed artifact with no `.cts` source of its own (it is hand-serialized',
'// from gsd-core/bin/shared/exit-codes.json by scripts/gen-exit-code-registry.cjs,',
'// from msd-core/bin/shared/exit-codes.json by scripts/gen-exit-code-registry.cjs,',
'// ADR-3889 §2, #3905/#3906), so tsc has nothing to compile for it. This file',
"// exists purely so `src/cli-exit.cts`'s `require('./exit-code-registry.cjs')`",
'// type-checks against the SAME shape the generated artifact actually exports',
@@ -521,7 +521,7 @@ function serializeDts(declarationPath) {
* only ever ASSIGNS variables can never trip an unset-variable check,
* regardless of what the caller's shell had in scope beforehand).
*
* Consumed by the bash scanners under scripts/ via `. gsd-core/bin/shared/
* Consumed by the bash scanners under scripts/ via `. msd-core/bin/shared/
* exit-codes.sh` (ADR-3889 Phase 4, #3908) so a shell caller resolves a
* symbolic name instead of hardcoding a literal integer that can silently
* drift from the registry.
@@ -534,14 +534,14 @@ function serializeSh(entries, declarationPath) {
`# Source of truth: ${relDeclaration}. Regenerate with:`,
'# node scripts/gen-exit-code-registry.cjs --write',
'#',
'# One `export EXIT_<NAME>=<code>` per gsd-core/bin/shared/exit-codes.json',
'# One `export EXIT_<NAME>=<code>` per msd-core/bin/shared/exit-codes.json',
'# entry (ADR-3889 §2, #3905/#3906/#3908). POSIX sh, safe under `set -u`:',
'# sourcing this file only ever ASSIGNS variables, never reads one, so it',
'# cannot trip an unset-variable check regardless of the caller\'s existing',
'# environment.',
'#',
'# Usage (from a scanner under scripts/):',
'# . "$(dirname "$0")/../gsd-core/bin/shared/exit-codes.sh"',
'# . "$(dirname "$0")/../msd-core/bin/shared/exit-codes.sh"',
'# exit "$EXIT_UNAVAILABLE"',
'',
].join('\n');

View File

@@ -3,21 +3,21 @@
/**
* Generates the `<error_codes>` and `<repair_actions>` tables in
* `gsd-core/workflows/health.md` from `src/health-diagnostic.cts`'s `RULES`
* `msd-core/workflows/health.md` from `src/health-diagnostic.cts`'s `RULES`
* table (Phase 11 follow-up, #3309 "Proposed behavior": "health.md's tables
* are generated rather than hand-maintained, closing the 16-vs-30+
* documentation gap structurally").
*
* Sources:
* - The 31 real rules in the compiled `RULES` array
* (`gsd-core/bin/lib/health-diagnostic.cjs`, built from
* (`msd-core/bin/lib/health-diagnostic.cjs`, built from
* `src/health-diagnostic.cts` + `src/health-diagnostic-rules/*.cts`),
* each carrying a static `description`/`repairable` (see
* `src/health-diagnostic-types.cts`'s `Rule` interface).
* - `PRECHECK_CODES` below — E001, E010, I010 — the three diagnostics
* `cmdValidateHealth` (`src/verify.cts`) emits as pre-checks OUTSIDE the
* rule table entirely (ADR-3180 §8.2 rule 4, "no precedence system" —
* see `.gsd/phase/refactor-3309-health-diagnostic-rule-table/40-design.md`,
* see `.msd/phase/refactor-3309-health-diagnostic-rule-table/40-design.md`,
* "Two guards that stay OUTSIDE the rule table entirely"). These will
* never appear in `RULES`, so they are a small, static, clearly-labeled
* list merged in here instead.
@@ -53,9 +53,9 @@ const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.resolve(__dirname, '..');
const HEALTH_MD_REL = 'gsd-core/workflows/health.md';
const HEALTH_MD_REL = 'msd-core/workflows/health.md';
const HEALTH_MD_PATH = path.join(ROOT, HEALTH_MD_REL);
const COMPILED_MODULE_REL = 'gsd-core/bin/lib/health-diagnostic.cjs';
const COMPILED_MODULE_REL = 'msd-core/bin/lib/health-diagnostic.cjs';
const COMPILED_MODULE_PATH = path.join(ROOT, COMPILED_MODULE_REL);
const ERROR_CODES_START = '<error_codes>';

View File

@@ -8,7 +8,7 @@
* straight from a raw, unbuilt clone (a hook is required by name via
* `require('./lib/cli-exit.js')` relative to the hook's own __dirname), so
* the generated file is compiled to a THROWAWAY outDir rather than read from
* gsd-core/bin/lib/ — reading the tracked build output would let a stale
* msd-core/bin/lib/ — reading the tracked build output would let a stale
* build produce a false green. This mirrors scripts/gen-scripts-cli-exit.cjs
* exactly (same compile-to-temp strategy, same banner/check/write shape);
* kept as a SIBLING script rather than folded into that one because
@@ -70,17 +70,17 @@ const BANNER = [
'// Why this copy exists: hooks/ runs straight from a raw, unbuilt clone — a',
'// shipped hook must be able to `require(\'./lib/cli-exit.js\')` relative to',
'// its own __dirname and terminate through `terminateNow` without depending',
'// on any build artifact. gsd-core/bin/lib/cli-exit.cjs is gitignored tsc',
'// on any build artifact. msd-core/bin/lib/cli-exit.cjs is gitignored tsc',
'// output and doubles as the build sentinel, so it cannot be required from',
'// here. `.js`, not `.cjs`, to match the hooks/lib/*.js convention. Hence one',
'// source, three emitted locations (gsd-core/bin/lib, scripts/lib, hooks/lib).',
'// source, three emitted locations (msd-core/bin/lib, scripts/lib, hooks/lib).',
'',
'',
].join('\n');
/** Compile the whole project to a throwaway outDir so the work tree is untouched. */
function compileToTemp() {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-hooks-cli-exit-'));
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-hooks-cli-exit-'));
try {
execFileSync(
process.execPath,

View File

@@ -8,7 +8,7 @@
* which in turn reuses buildParityManifest's exact exclusion set (issue
* #2266) so the file-set fixtures here and the golden-install-parity content
* fixtures never diverge on which files they cover. The authoritative test
* gate remains `gsd-test run`, never a local `node --test`.
* gate remains `msd-test run`, never a local `node --test`.
*
* Usage: node scripts/gen-install-tree-fixtures.cjs [runtime ...]
*/
@@ -29,7 +29,7 @@ function cleanup(root) {
}
// Regenerate the fixture for every runtime in RUNTIME_META. Needed when a
// SHARED gsd-core payload file (e.g. model-catalog.json, capability-registry)
// SHARED msd-core payload file (e.g. model-catalog.json, capability-registry)
// changes content — its path appears in every runtime's manifest, so all
// fixtures must be recaptured together. Usage:
// node scripts/gen-install-tree-fixtures.cjs [runtime ...]
@@ -61,8 +61,8 @@ for (const runtime of targets) {
}
// Also regenerate the claude LOCAL legacy-layout fixture (claude-local.json).
// This layout is distinct from the global install (commands/gsd-*.md +
// agents/gsd-*.md) and has its own snapshot assertion in the test harness.
// This layout is distinct from the global install (commands/msd-*.md +
// agents/msd-*.md) and has its own snapshot assertion in the test harness.
const { configDir: localConfigDir, root: localRoot } = runMinimalInstall({ runtime: 'claude', scope: 'local' });
let localActual;
try {

View File

@@ -25,30 +25,30 @@ const FAMILIES = [
{
name: 'agents',
dir: path.join(ROOT, 'agents'),
filter: (f) => /^gsd-.*\.md$/.test(f),
filter: (f) => /^msd-.*\.md$/.test(f),
toName: (f) => f.replace(/\.md$/, ''),
},
{
name: 'commands',
dir: path.join(ROOT, 'commands', 'gsd'),
dir: path.join(ROOT, 'commands', 'msd'),
filter: (f) => f.endsWith('.md'),
toName: (f) => '/gsd-' + f.replace(/\.md$/, ''),
toName: (f) => '/msd-' + f.replace(/\.md$/, ''),
},
{
name: 'workflows',
dir: path.join(ROOT, 'gsd-core', 'workflows'),
dir: path.join(ROOT, 'msd-core', 'workflows'),
filter: (f) => f.endsWith('.md'),
toName: (f) => f,
},
{
name: 'references',
dir: path.join(ROOT, 'gsd-core', 'references'),
dir: path.join(ROOT, 'msd-core', 'references'),
filter: (f) => f.endsWith('.md'),
toName: (f) => f,
},
{
name: 'cli_modules',
dir: path.join(ROOT, 'gsd-core', 'bin', 'lib'),
dir: path.join(ROOT, 'msd-core', 'bin', 'lib'),
filter: (f) => f.endsWith('.cjs'),
toName: (f) => f,
},
@@ -64,8 +64,8 @@ const FAMILIES = [
* One-level-nested families (#2996, epic #1671 Phase 6.5).
*
* `buildManifest`'s flat `readdirSync` + `isFile()` walk cannot see a workflow's
* own sub-files, so `gsd-core/workflows/<wf>/steps/*.md` (the fragment tree
* extracted by Phases 6.1-6.3) and `gsd-core/workflows/<wf>/modes/*.md` (the
* own sub-files, so `msd-core/workflows/<wf>/steps/*.md` (the fragment tree
* extracted by Phases 6.1-6.3) and `msd-core/workflows/<wf>/modes/*.md` (the
* #717 progressive-disclosure pattern) shipped invisible to both the manifest
* and `docs/INVENTORY.md` — exactly the `DEFECT.INVENTORY-DRIFT` class.
*
@@ -81,25 +81,25 @@ const FAMILIES = [
const NESTED_FAMILIES = [
{
name: 'workflow_modes',
root: path.join(ROOT, 'gsd-core', 'workflows'),
root: path.join(ROOT, 'msd-core', 'workflows'),
subdir: 'modes',
filter: (f) => f.endsWith('.md'),
},
{
name: 'workflow_steps',
root: path.join(ROOT, 'gsd-core', 'workflows'),
root: path.join(ROOT, 'msd-core', 'workflows'),
subdir: 'steps',
filter: (f) => f.endsWith('.md'),
},
{
name: 'workflow_detail',
root: path.join(ROOT, 'gsd-core', 'workflows'),
root: path.join(ROOT, 'msd-core', 'workflows'),
subdir: 'detail',
filter: (f) => f.endsWith('.md'),
},
{
name: 'workflow_templates',
root: path.join(ROOT, 'gsd-core', 'workflows'),
root: path.join(ROOT, 'msd-core', 'workflows'),
subdir: 'templates',
filter: (f) => f.endsWith('.md'),
},
@@ -133,7 +133,7 @@ function statOrNull(p) {
* Collect `<dir>/<subdir>/<file>` entries as `<subdir>/<file>` keys — ONE level of
* subdirectory beneath `dir` itself, where the subdirectory's NAME is the thing being
* collected (unlike `collectNested`, there is no fixed subdir name to look for; every
* child directory of `dir` is scanned). This is what makes `gsd-core/bin/lib/<subdir>/*.cjs`
* child directory of `dir` is scanned). This is what makes `msd-core/bin/lib/<subdir>/*.cjs`
* (e.g. `health-diagnostic-rules/`, `installer-migrations/`, `host-integration-adapters/`,
* `observability/`) visible to the `cli_modules` family, mirroring the shape
* `docs/INVENTORY.md`'s CLI Modules table already uses for these files.

View File

@@ -2,8 +2,8 @@
'use strict';
/**
* gen-loop-host-contract.cjs — generates gsd-core/bin/lib/loop-host-contract.cjs
* from the <!-- gsd:loop-host ... --> blocks in the five step workflows.
* gen-loop-host-contract.cjs — generates msd-core/bin/lib/loop-host-contract.cjs
* from the <!-- msd:loop-host ... --> blocks in the five step workflows.
*
* Usage:
* node scripts/gen-loop-host-contract.cjs # print to stdout
@@ -20,12 +20,12 @@ const fs = require('node:fs');
const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { escapeRegex: escapeRegExp } = require('../gsd-core/bin/lib/pattern.cjs');
const { normalizeEol } = require('../gsd-core/bin/lib/text-lines.cjs');
const { escapeRegex: escapeRegExp } = require('../msd-core/bin/lib/pattern.cjs');
const { normalizeEol } = require('../msd-core/bin/lib/text-lines.cjs');
const ROOT = path.resolve(__dirname, '..');
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
const CONTRACT_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'loop-host-contract.cjs');
const WORKFLOWS_DIR = path.join(ROOT, 'msd-core', 'workflows');
const CONTRACT_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'loop-host-contract.cjs');
// The five step workflows in pipeline order
const STEP_WORKFLOWS = [
@@ -69,13 +69,13 @@ const EXPECTED_POINTS_BY_STEP = {
// Role → agent-name mapping used for cross-check.
// Each non-orchestrator role must correspond to an actual agent reference in
// the workflow file (e.g. gsd-planner, gsd-executor, gsd-verifier, etc.).
// the workflow file (e.g. msd-planner, msd-executor, msd-verifier, etc.).
const ROLE_TO_AGENT = {
researcher: 'gsd-phase-researcher',
planner: 'gsd-planner',
checker: 'gsd-plan-checker',
executor: 'gsd-executor',
verifier: 'gsd-verifier',
researcher: 'msd-phase-researcher',
planner: 'msd-planner',
checker: 'msd-plan-checker',
executor: 'msd-executor',
verifier: 'msd-verifier',
};
// #4740 — Role → family mapping. Three families: orchestration, planning,
@@ -98,12 +98,12 @@ const EXPECTED_FAMILY_BY_STEP = {
// ─── Parser ───────────────────────────────────────────────────────────────────
/**
* Parse a single <!-- gsd:loop-host ... --> block from file content.
* Parse a single <!-- msd:loop-host ... --> block from file content.
* Returns a plain object with keys: step, points[], agentRoles[], produces[], consumes[].
* Throws a descriptive error if the block is malformed or missing.
*
* Block format (one key: value per line, comma-separated list values):
* <!-- gsd:loop-host
* <!-- msd:loop-host
* step: plan
* points: plan:pre, plan:post
* agent-roles: researcher, planner, checker
@@ -119,14 +119,14 @@ const EXPECTED_FAMILY_BY_STEP = {
*/
function parseLoopHostBlock(content, fileName) {
// FIX 2: Detect ALL marker blocks — more than one is a hard error.
const blockRe = /<!--\s*gsd:loop-host\s*([\s\S]*?)-->/g;
const blockRe = /<!--\s*msd:loop-host\s*([\s\S]*?)-->/g;
const allMatches = Array.from(content.matchAll(blockRe));
if (allMatches.length === 0) {
throw new Error(fileName + ': missing <!-- gsd:loop-host ... --> block');
throw new Error(fileName + ': missing <!-- msd:loop-host ... --> block');
}
if (allMatches.length > 1) {
throw new Error(
fileName + ': expected exactly one gsd:loop-host marker block, found ' + allMatches.length,
fileName + ': expected exactly one msd:loop-host marker block, found ' + allMatches.length,
);
}
@@ -146,7 +146,7 @@ function parseLoopHostBlock(content, fileName) {
}
for (const key of RECOGNIZED_KEYS) {
if (keyCounts[key] > 1) {
throw new Error(fileName + ': duplicate key \'' + key + '\' in gsd:loop-host marker');
throw new Error(fileName + ': duplicate key \'' + key + '\' in msd:loop-host marker');
}
}
@@ -165,7 +165,7 @@ function parseLoopHostBlock(content, fileName) {
return raw.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
}
}
throw new Error(fileName + ': gsd:loop-host block missing required field "' + key + '"');
throw new Error(fileName + ': msd:loop-host block missing required field "' + key + '"');
}
function parseScalar(key) {
@@ -176,12 +176,12 @@ function parseLoopHostBlock(content, fileName) {
const colonIdx = trimmed.indexOf(':');
const val = trimmed.slice(colonIdx + 1).trim();
if (val === '') {
throw new Error(fileName + ': gsd:loop-host block field "' + key + '" must be a non-empty string');
throw new Error(fileName + ': msd:loop-host block field "' + key + '" must be a non-empty string');
}
return val;
}
}
throw new Error(fileName + ': gsd:loop-host block missing required field "' + key + '"');
throw new Error(fileName + ': msd:loop-host block missing required field "' + key + '"');
}
const step = parseScalar('step');
@@ -191,10 +191,10 @@ function parseLoopHostBlock(content, fileName) {
const consumes = parseField('consumes');
if (points.length === 0) {
throw new Error(fileName + ': gsd:loop-host block "points" must have at least one value');
throw new Error(fileName + ': msd:loop-host block "points" must have at least one value');
}
if (agentRoles.length === 0) {
throw new Error(fileName + ': gsd:loop-host block "agent-roles" must have at least one value');
throw new Error(fileName + ': msd:loop-host block "agent-roles" must have at least one value');
}
return {
@@ -227,8 +227,8 @@ function crossCheckRoles(content, agentRoles, fileName) {
);
continue;
}
// FIX 3: Use word-boundary match so "gsd-plan-checker-v2" does NOT satisfy a required
// "gsd-plan-checker". Treat '-' as part of the token: boundary = start/end of string or
// FIX 3: Use word-boundary match so "msd-plan-checker-v2" does NOT satisfy a required
// "msd-plan-checker". Treat '-' as part of the token: boundary = start/end of string or
// a character that is neither \w nor '-'.
// Note: this is a presence check (any reference in the file), not a spawn-site check —
// a known limitation; spawn-site checks would require AST-level analysis.
@@ -385,7 +385,7 @@ function buildContract(workflowsDir) {
// Validate the declared step matches the expected step for this file
if (entry.step !== step) {
allErrors.push(
file + ': gsd:loop-host block declares step "' + entry.step +
file + ': msd:loop-host block declares step "' + entry.step +
'" but expected "' + step + '"',
);
}
@@ -488,7 +488,7 @@ function main() {
if (!fs.existsSync(CONTRACT_PATH)) {
process.stderr.write(
'gsd-core/bin/lib/loop-host-contract.cjs does not exist. Run:\n' +
'msd-core/bin/lib/loop-host-contract.cjs does not exist. Run:\n' +
' node scripts/gen-loop-host-contract.cjs --write\n',
);
throw new ExitError(1);
@@ -498,13 +498,13 @@ function main() {
// FIX 4: Compare full content (no generated-by stripping) so header drift is caught.
if (normalizeEol(committed) !== normalizeEol(live)) {
process.stderr.write(
'gsd-core/bin/lib/loop-host-contract.cjs is stale. Run:\n' +
'msd-core/bin/lib/loop-host-contract.cjs is stale. Run:\n' +
' node scripts/gen-loop-host-contract.cjs --write\n',
);
throw new ExitError(1);
}
process.stdout.write('gsd-core/bin/lib/loop-host-contract.cjs is up to date.\n');
process.stdout.write('msd-core/bin/lib/loop-host-contract.cjs is up to date.\n');
} else if (flag === '--write') {
let contract;
try {
@@ -539,8 +539,8 @@ function main() {
* a separate hardcoded list.
*/
const HOST_LOOP_FILES = STEP_WORKFLOWS.flatMap(({ file, auxiliaryHosts = [] }) => [
'gsd-core/workflows/' + file,
...auxiliaryHosts.map((host) => 'gsd-core/workflows/' + host.file),
'msd-core/workflows/' + file,
...auxiliaryHosts.map((host) => 'msd-core/workflows/' + host.file),
]);
/**
@@ -577,7 +577,7 @@ const HOOK_KINDS = ['contribution', 'step', 'gate'];
* call site, up to the next call site or the region cap, judged LINE by line:
*
* - A deferral line (one carrying an `@`-included path to the generic
* contract, e.g. `@gsd-core/references/loop-hook-dispatch.md`) that names a
* contract, e.g. `@msd-core/references/loop-hook-dispatch.md`) that names a
* kind (`kind == "step"`) covers that kind; a deferral line with no kind
* discriminator ("apply each entry") covers every kind only when no role
* target is required. With `expectedInto`, the same segment must explicitly

View File

@@ -260,7 +260,7 @@ const ALWAYS_REAL_OS = new Map([
/**
* macOS-specific detection categories (#4593, design doc
* .gsd/phase/chore-4593-macos-conformance-tier/40-design.md). Built new,
* .msd/phase/chore-4593-macos-conformance-tier/40-design.md). Built new,
* rather than reusing CATEGORIES above minus its Windows-specific entries,
* because that naive exclusion barely narrows anything (measured: 546 -> 424
* files, 78%) — most files match multiple general-tier signals simultaneously

View File

@@ -2,8 +2,8 @@
'use strict';
/**
* gen-plugin-skills.cjs — generates skills/gsd-<stem>/SKILL.md from
* commands/gsd/*.md using convertClaudeCommandToClaudeSkill.
* gen-plugin-skills.cjs — generates skills/msd-<stem>/SKILL.md from
* commands/msd/*.md using convertClaudeCommandToClaudeSkill.
*
* Usage:
* node scripts/gen-plugin-skills.cjs # print summary to stdout
@@ -11,13 +11,13 @@
* node scripts/gen-plugin-skills.cjs --check # exit 1 if committed skills/ is stale
*
* #1596 Phase B-provide. The Claude Code plugin contract discovers skills from
* a skills/ directory (plugins-reference). GSD's source-of-truth commands live
* in commands/gsd/*.md (command frontmatter); this script converts each to
* a skills/ directory (plugins-reference). MSD's source-of-truth commands live
* in commands/msd/*.md (command frontmatter); this script converts each to
* skill format using the same convertClaudeCommandToClaudeSkill the file-copy
* installer uses, producing a build-generated skills/ dir that ships in the
* npm package and serves plugin-only installs.
*
* Depends on: gsd-core/bin/lib/runtime-artifact-conversion.cjs (compiled from
* Depends on: msd-core/bin/lib/runtime-artifact-conversion.cjs (compiled from
* src/runtime-artifact-conversion.cts by `npm run build:lib`). Must run AFTER
* build:lib in the build chain.
*/
@@ -27,14 +27,14 @@ const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.resolve(__dirname, '..');
const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd');
const COMMANDS_DIR = path.join(ROOT, 'commands', 'msd');
const SKILLS_DIR = path.join(ROOT, 'skills');
const CONVERSION_MODULE = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'runtime-artifact-conversion.cjs');
const PREFIX = 'gsd-';
const CONVERSION_MODULE = path.join(ROOT, 'msd-core', 'bin', 'lib', 'runtime-artifact-conversion.cjs');
const PREFIX = 'msd-';
const RUNTIME = 'claude';
function generateSkills(conversion) {
const cmdNames = conversion.readGsdCommandNames();
const cmdNames = conversion.readMsdCommandNames();
const files = fs.readdirSync(COMMANDS_DIR).filter(f => f.endsWith('.md'));
const results = [];
for (const file of files) {

View File

@@ -41,7 +41,7 @@ const path = require('node:path');
const REPO_ROOT = path.resolve(__dirname, '..');
const OUT_PATH = path.join(REPO_ROOT, 'tests', 'fixtures', 'prompt-budget-parity', 'corpus.json');
const IMPL_PATH = path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'prompt-budget.cjs');
const IMPL_PATH = path.join(REPO_ROOT, 'msd-core', 'bin', 'lib', 'prompt-budget.cjs');
/** Corpus schema version. Bump only on a deliberate shape change. */
const CORPUS_VERSION = 1;

View File

@@ -13,7 +13,7 @@
*
* NOT to be confused with `scripts/gen-capability-registry.cjs`: that script
* generates the RUNTIME capability manifest consumed by the host at runtime
* (`gsd-core/bin/lib/capability-registry.cjs`, built from every
* (`msd-core/bin/lib/capability-registry.cjs`, built from every
* `capabilities/<id>/capability.json` declaration). THIS script instead
* generates the human-facing DOCUMENTATION catalog pages
* (`docs/registries/*.md`) from the third-party discoverability registry
@@ -35,7 +35,7 @@ const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { renderMarkdown } = require('./registry-schema.cjs');
const { normalizeEol } = require('../gsd-core/bin/lib/text-lines.cjs');
const { normalizeEol } = require('../msd-core/bin/lib/text-lines.cjs');
const SOURCES = [
{ type: 'capability', jsonFile: 'capabilities.json', mdFile: 'capability-registry.md' },

View File

@@ -4,7 +4,7 @@
* compile of src/cli-exit.cts.
*
* ADR-3889 Phase 0 (#3904): scripts/lib/cli-exit.cjs used to be a hand-written
* fork of gsd-core/bin/lib/cli-exit.cjs (the compiled artifact of
* fork of msd-core/bin/lib/cli-exit.cjs (the compiled artifact of
* src/cli-exit.cts). The two drifted — only the .cts copy routed a
* non-ExitError throw through getJsonErrorMode() to emit a structured
* { ok:false, reason, message } envelope. This script makes scripts/lib/cli-exit.cjs
@@ -14,7 +14,7 @@
* scripts/ runs straight from the repo checkout and must work on an unbuilt
* clone (scripts/check-env.cjs requires this file before any build runs), so
* the generated file is compiled to a THROWAWAY outDir rather than read from
* gsd-core/bin/lib/ — reading the tracked build output would let a stale build
* msd-core/bin/lib/ — reading the tracked build output would let a stale build
* produce a false green.
*
* Usage:
@@ -58,7 +58,7 @@ const BANNER = [
'//',
'// Why this copy exists: scripts/ runs straight from the repo checkout and must',
'// work on an unbuilt clone — 64+ scripts require this file, including',
'// check-env.cjs, which runs before any build. gsd-core/bin/lib/cli-exit.cjs is',
'// check-env.cjs, which runs before any build. msd-core/bin/lib/cli-exit.cjs is',
'// gitignored tsc output and doubles as the build sentinel, so it cannot be',
'// required from here. Hence one source, two emitted locations.',
'',
@@ -67,7 +67,7 @@ const BANNER = [
/** Compile the whole project to a throwaway outDir so the work tree is untouched. */
function compileToTemp() {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-scripts-cli-exit-'));
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-scripts-cli-exit-'));
try {
execFileSync(
process.execPath,

View File

@@ -2,22 +2,22 @@
'use strict';
/**
* gen-section-manifest.cjs — generates gsd-core/workflows/section-manifest.json
* from the `<!-- gsd:section -->` markers in gsd-core/workflows/*.md (ADR-1671
* gen-section-manifest.cjs — generates msd-core/workflows/section-manifest.json
* from the `<!-- msd:section -->` markers in msd-core/workflows/*.md (ADR-1671
* epic #1671; Phase 5 / issue #2932 introduced the artifact,
* `.gsd/phase/chore-2932-init-section-manifest/40-design.md`; Phase 6.1 /
* `.msd/phase/chore-2932-init-section-manifest/40-design.md`; Phase 6.1 /
* issue #2992 generalized it from single-workflow to PER-WORKFLOW,
* `.gsd/phase/chore-2992-widen-when-vocabulary/40-design.md`).
* `.msd/phase/chore-2992-widen-when-vocabulary/40-design.md`).
*
* Reuses `parseWorkflowSections` from the compiled `workflow-fragments.cjs`
* (src/workflow-fragments.cts, Phase 3 / #2930) UNCHANGED — this module never
* re-implements marker parsing (design "Rejected #6"; a second parser is the
* `DEFECT.GENERATIVE-FIX` divergence class).
*
* The committed artifact is placed INSIDE the `gsd-core/` tree (not `docs/`,
* The committed artifact is placed INSIDE the `msd-core/` tree (not `docs/`,
* unlike `docs/CONTEXT-INDEX.json`/`docs/INVENTORY-MANIFEST.json`) because it
* must SHIP: `bin/install.js`'s `copyWithPathReplacement` only copies
* `gsd-core/`, and the init CLI's run-time selection reads this artifact
* `msd-core/`, and the init CLI's run-time selection reads this artifact
* from the INSTALLED tree, not the dev repo. `copyWithPathReplacement`
* only runs `composeWorkflow`/converters on `*.md` — a `.json` leaf falls
* through to a plain `fs.copyFileSync`, so the artifact ships byte-identical.
@@ -58,7 +58,7 @@
* (most workflows today) contributes no key to `workflows` and is never
* orphan-checked — orphan-checking is scoped only to a workflow's OWN
* `steps/` directory, and only for workflows that declare at least one
* `gsd:section` marker.
* `msd:section` marker.
*
* `--check` fails closed (exit 1, never a stack trace) on:
* - the compiled `workflow-fragments.cjs` dependency being unbuilt/unloadable
@@ -100,7 +100,7 @@ const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.resolve(__dirname, '..');
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
const WORKFLOWS_DIR = path.join(ROOT, 'msd-core', 'workflows');
const MANIFEST_PATH = path.join(WORKFLOWS_DIR, 'section-manifest.json');
// ─── Typed reason enum (CONTRIBUTING.md "Prohibited: Raw Text Matching") ───────
@@ -129,7 +129,7 @@ const REASON = Object.freeze({
// ─── Loaders ──────────────────────────────────────────────────────────────────
const WORKFLOW_FRAGMENTS_LIB_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'workflow-fragments.cjs');
const WORKFLOW_FRAGMENTS_LIB_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'workflow-fragments.cjs');
/**
* Load the compiled workflow-fragments library. The artifact is a gitignored
@@ -239,7 +239,7 @@ class ManifestBuildError extends ExitError {
}
/**
* Scan `workflowsDir` for `.md` files carrying `gsd:section` markers and
* Scan `workflowsDir` for `.md` files carrying `msd:section` markers and
* build the live (freshly-derived) manifest: `{ workflows: { <name>:
* [{id, when, read}], ... } }` (#2992 Phase 6.1). Workflow keys are
* serialized in sorted (filename) order; a workflow's own sections stay in
@@ -249,7 +249,7 @@ class ManifestBuildError extends ExitError {
* Throws `ManifestBuildError` on any fail-closed condition (missing step
* file, orphan step file, unparseable source).
*
* @param {string} workflowsDir - defaults to the real repo-root gsd-core/workflows/
* @param {string} workflowsDir - defaults to the real repo-root msd-core/workflows/
* @param {string} repoRoot - root `read` paths are computed relative to
* @returns {{ workflows: Record<string, Array<{id: string, when: string, read: string}>> }}
*/
@@ -311,7 +311,7 @@ function buildFreshManifest(workflowsDir = WORKFLOWS_DIR, repoRoot = ROOT) {
throw new ManifestBuildError(
REASON.FAIL_ORPHAN_STEP_FILE,
relOrphanPath,
`${relOrphanPath} is not referenced by any gsd:section marker or reachable "steps/" reference in ${relSourcePath}`,
`${relOrphanPath} is not referenced by any msd:section marker or reachable "steps/" reference in ${relSourcePath}`,
);
}

View File

@@ -2,14 +2,14 @@
'use strict';
/**
* Generates the schema-derived MARKED REGIONS inside `gsd-core/templates/state.md`
* Generates the schema-derived MARKED REGIONS inside `msd-core/templates/state.md`
* and the five `docs/{,ja-JP/,zh-CN/,ko-KR/,pt-BR/}reference/state-md.md` pages
* from `STATE_FIELD_SCHEMA` (`src/state-md-schema.cts`, ADR-3473 §8.8, #3873).
*
* WHY THIS EXISTS. Four hand-maintained declarations of "which STATE.md keys
* exist and what they carry" already disagreed (see `src/state-md-schema.cts`'s
* own docstring for the `last_activity` case). Two of the SIX places named in
* `.gsd/phase/feat-3873-state-md-schema/40-design.md`'s table are documents,
* `.msd/phase/feat-3873-state-md-schema/40-design.md`'s table are documents,
* not code: the shipped template and the reference docs. This generator is
* their half of the consolidation. `FIELD_CLASSIFICATION` /
* `FRONTMATTER_BODY_SOURCE` / `FRONTMATTER_KEY_TO_BODY_LABEL` project from the
@@ -26,7 +26,7 @@
*
* Two regions are declared today:
* - `frontmatter` — the initial YAML frontmatter block in
* `gsd-core/templates/state.md`, the block a new project starts from.
* `msd-core/templates/state.md`, the block a new project starts from.
* - `status-lifecycle` — the `### Status lifecycle (ADR-2207)` section
* documenting the `status` field's lifecycle. This is the section
* ISSUE #3873's own design doc found missing from all four locale
@@ -78,7 +78,7 @@ const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const REPO_ROOT = path.resolve(__dirname, '..');
const SCHEMA_LIB_PATH = path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'state-md-schema.cjs');
const SCHEMA_LIB_PATH = path.join(REPO_ROOT, 'msd-core', 'bin', 'lib', 'state-md-schema.cjs');
const MARKER_TAG = 'STATE-MD-SCHEMA';
@@ -101,7 +101,7 @@ const REASON = Object.freeze({
* only the template carries `frontmatter`.
*/
const TARGETS = Object.freeze([
{ key: 'template', relPath: path.join('gsd-core', 'templates', 'state.md'), regions: ['frontmatter'] },
{ key: 'template', relPath: path.join('msd-core', 'templates', 'state.md'), regions: ['frontmatter'] },
{ key: 'en', relPath: path.join('docs', 'reference', 'state-md.md'), locale: 'en', regions: ['status-lifecycle', 'cardinality'] },
{ key: 'ja-JP', relPath: path.join('docs', 'ja-JP', 'reference', 'state-md.md'), locale: 'ja-JP', regions: ['status-lifecycle', 'cardinality'] },
{ key: 'zh-CN', relPath: path.join('docs', 'zh-CN', 'reference', 'state-md.md'), locale: 'zh-CN', regions: ['status-lifecycle', 'cardinality'] },
@@ -118,7 +118,7 @@ function endMarker(region) {
return `<!-- ${MARKER_TAG}:END:${region} -->`;
}
// ─── Frontmatter region (gsd-core/templates/state.md) ──────────────────────
// ─── Frontmatter region (msd-core/templates/state.md) ──────────────────────
/**
* The subset of `STATE_FIELD_SCHEMA` keys a FRESH project's initial STATE.md
@@ -126,7 +126,7 @@ function endMarker(region) {
* future schema change drops one of these keys, rendering throws rather than
* silently emitting a template field the schema no longer recognizes.
*/
const TEMPLATE_FRONTMATTER_FIELDS = Object.freeze(['gsd_state_version', 'status', 'progress']);
const TEMPLATE_FRONTMATTER_FIELDS = Object.freeze(['msd_state_version', 'status', 'progress']);
function renderFrontmatterRegion(schema) {
for (const field of TEMPLATE_FRONTMATTER_FIELDS) {
@@ -149,7 +149,7 @@ function renderFrontmatterRegion(schema) {
return [
'```markdown',
'---',
"gsd_state_version: '1.0' # placeholder; syncStateFrontmatter overwrites on first state.* call",
"msd_state_version: '1.0' # placeholder; syncStateFrontmatter overwrites on first state.* call",
'status: planning',
'progress:',
' total_phases: 0',

View File

@@ -10,14 +10,14 @@
// .test.cjs) both scored 1. This script replaces the guess with measurement.
//
// Input is one or more node:test reporter event streams as emitted by
// `gsd-test` (`~/.local/state/gsd-test/runs/<run-id>/test-events-<os>-node<v>
// `msd-test` (`~/.local/state/msd-test/runs/<run-id>/test-events-<os>-node<v>
// .jsonl`). Each stream carries one `test:summary` event per test FILE, whose
// `data.duration_ms` is that file's total wall-clock and whose `data.file` is
// its absolute in-container path.
//
// Usage:
// node scripts/gen-test-timings.cjs <events.jsonl> [<events.jsonl> ...]
// node scripts/gen-test-timings.cjs ~/.local/state/gsd-test/runs/*/test-events-*.jsonl
// node scripts/gen-test-timings.cjs ~/.local/state/msd-test/runs/*/test-events-*.jsonl
// node scripts/gen-test-timings.cjs events.jsonl --out tests/test-timings.json
//
// When several streams are supplied (multiple lanes, e.g. node22 + node24), a

View File

@@ -2,14 +2,14 @@
'use strict';
/**
* Single source for GSD's published-package coordinates (issue #498).
* Single source for MSD's published-package coordinates (issue #498).
*
* `deriveIdentity(pkg)` is the pure core: it turns a parsed package.json into
* the coordinate record every consumer needs. The generated runtime module
* `gsd-core/bin/lib/package-identity.cjs` bakes those values at build
* `msd-core/bin/lib/package-identity.cjs` bakes those values at build
* time, because the installed tree carries no package.json with a `.name` —
* the only ones GSD stages are synthetic `{"type":"commonjs"}` markers, which
* since #2544 live inside GSD's own directories (`hooks/`, and the native
* the only ones MSD stages are synthetic `{"type":"commonjs"}` markers, which
* since #2544 live inside MSD's own directories (`hooks/`, and the native
* plugin dir) rather than at the config root — so a runtime
* `require('package.json').name` resolves to `undefined` (the #378 bug this
* seam retires). Baking from package.json reconciles #378 (renames survive)
@@ -53,7 +53,7 @@ function deriveIdentity(pkg = {}) {
? `https://raw.githubusercontent.com/${repoSlug}/main/CHANGELOG.md`
: '';
const cacheSlug = slugifyPackageName(packageName);
const updateCacheFileName = cacheSlug ? `gsd-update-check-${cacheSlug}.json` : 'gsd-update-check.json';
const updateCacheFileName = cacheSlug ? `msd-update-check-${cacheSlug}.json` : 'msd-update-check.json';
return { packageName, binName, repoSlug, repoUrl, changelogRawUrl, cacheSlug, updateCacheFileName };
}
@@ -72,7 +72,7 @@ function formatManualInstall({ packageName, binName, scope, runtime } = {}) {
const GENERATED_HEADER =
'// @generated by scripts/generate-package-identity.cjs from package.json — DO NOT EDIT.\n' +
'// Single source for GSD package coordinates (issue #498). Regenerate with:\n' +
'// Single source for MSD package coordinates (issue #498). Regenerate with:\n' +
'// node scripts/generate-package-identity.cjs\n';
/**
@@ -119,7 +119,7 @@ function main() {
const args = process.argv.slice(2);
const check = args.includes('--check');
const pkg = require(path.join(__dirname, '..', 'package.json'));
const out = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'package-identity.cjs');
const out = path.join(__dirname, '..', 'msd-core', 'bin', 'lib', 'package-identity.cjs');
const rendered = render(deriveIdentity(pkg));
if (check) {
// Compare normalized content so a CRLF checkout (Windows, no .gitattributes

View File

@@ -3,7 +3,7 @@ set -euo pipefail
usage() {
cat <<'EOF'
Install the private golem15 GSD build for Claude Code and Codex.
Install the private golem15 MSD build for Claude Code and Codex.
Grok Build consumes the shared Codex skills or Claude compatibility surface.
Usage:
@@ -12,14 +12,14 @@ Usage:
Options:
--update Pull origin/next with --ff-only before installing.
--skip-deps Do not run npm ci.
--profile PROFILE GSD profile: core, standard, or full (default: full).
--profile PROFILE MSD profile: core, standard, or full (default: full).
-h, --help Show this help.
EOF
}
update=false
install_deps=true
profile="${GSD_PROFILE:-full}"
profile="${MSD_PROFILE:-full}"
while (($#)); do
case "$1" in
@@ -63,7 +63,7 @@ node_major=${node_version#v}
node_major=${node_major%%.*}
if [[ ! "$node_major" =~ ^[0-9]+$ ]] || ((node_major < 24)); then
cat >&2 <<EOF
ERROR: this GSD checkout requires Node.js 24 or newer.
ERROR: this MSD checkout requires Node.js 24 or newer.
Active Node: ${node_version:-not found}
Install/activate Node 24, then rerun this script.
@@ -84,16 +84,16 @@ fi
echo "==> Building runtime hooks from the private checkout"
npm run build:hooks
echo "==> Installing GSD for Claude Code (profile: $profile)"
echo "==> Installing MSD for Claude Code (profile: $profile)"
node bin/install.js --claude --global "--profile=$profile"
echo "==> Installing GSD for Codex (profile: $profile)"
echo "==> Installing MSD for Codex (profile: $profile)"
node bin/install.js --codex --global "--profile=$profile"
claude_root=${CLAUDE_CONFIG_DIR:-"$HOME/.claude"}
codex_root=${CODEX_HOME:-"$HOME/.codex"}
agents_root=${GROK_AGENTS_HOME:-"$HOME/.agents"}
recovery_rel=gsd-core/workflows/plan-phase/steps/agent-recovery-and-timing.md
recovery_rel=msd-core/workflows/plan-phase/steps/agent-recovery-and-timing.md
for install_root in "$claude_root" "$codex_root"; do
if [[ ! -f "$install_root/$recovery_rel" ]]; then
@@ -117,19 +117,19 @@ fi
if command -v grok >/dev/null 2>&1; then
grok_report=$(mktemp)
trap 'rm -f "$grok_report"' EXIT
shared_skill="$agents_root/skills/gsd-plan-phase/SKILL.md"
claude_skill="$claude_root/skills/gsd-plan-phase/SKILL.md"
shared_skill="$agents_root/skills/msd-plan-phase/SKILL.md"
claude_skill="$claude_root/skills/msd-plan-phase/SKILL.md"
if grok inspect --json >"$grok_report" 2>/dev/null \
&& { grep -Fq "$shared_skill" "$grok_report" || grep -Fq "$claude_skill" "$grok_report"; }; then
echo "==> Grok verified: gsd-plan-phase skill discovered"
echo "==> Grok verified: msd-plan-phase skill discovered"
else
echo "WARNING: Grok did not report a compatible gsd-plan-phase skill; restart Grok and run 'grok inspect'." >&2
echo "WARNING: Grok did not report a compatible msd-plan-phase skill; restart Grok and run 'grok inspect'." >&2
fi
fi
cat <<EOF
Private GSD installation complete.
Private MSD installation complete.
Source: $repo_root
Claude: $claude_root
Codex: $codex_root

View File

@@ -7,7 +7,7 @@
const POSSIBLE_DUPLICATE_LABEL = 'possible-duplicate';
const HUMAN_REVIEW_LABEL = 'needs-maintainer-review';
const CHALLENGE_MARKER = '<!-- gsd-dedupe-challenge -->';
const CHALLENGE_MARKER = '<!-- msd-dedupe-challenge -->';
const DEFAULT_WINDOW_HOURS = 24;
const DEFAULT_THRESHOLD = 0.6;
const DEFAULT_MAX_CANDIDATES = 5;

View File

@@ -1,29 +1,29 @@
#!/usr/bin/env node
'use strict';
const { PACKAGE_NAME } = require('../gsd-core/bin/lib/package-identity.cjs');
const { PACKAGE_NAME } = require('../msd-core/bin/lib/package-identity.cjs');
/**
* Version gate for bug-report issues.
*
* GitHub Issue Forms enforce `required: true` only in the web form; issues
* filed via the REST API, `gh issue create`, or automated/AI reporters can
* omit the GSD Version field entirely. This module provides the pure logic
* omit the MSD Version field entirely. This module provides the pure logic
* for detecting bug reports missing a usable version so a workflow can
* auto-close them. See .github/workflows/version-gate.yml.
*/
const BUG_LABEL = 'bug';
const NEEDS_VERSION_LABEL = 'needs-version';
const VERSION_GATE_MARKER = '<!-- gsd-version-gate -->';
const VERSION_GATE_MARKER = '<!-- msd-version-gate -->';
// Labels that opt an issue out of the version gate.
const EXEMPT_LABELS = ['version-exempt'];
// Heading GitHub renders for the bug template's `label: GSD Version` field.
// Heading GitHub renders for the bug template's `label: MSD Version` field.
// Issue Forms render input labels as `### <label>`; matches the exact heading
// `### GSD Version` (any heading level) — bare `### Version` is NOT matched.
const VERSION_HEADING_RE = /^#{1,6}\s*GSD\s+Version\s*$/i;
// `### MSD Version` (any heading level) — bare `### Version` is NOT matched.
const VERSION_HEADING_RE = /^#{1,6}\s*MSD\s+Version\s*$/i;
// GitHub's placeholder for an empty optional form field.
const NO_RESPONSE_RE = /^_no response_$/i;
@@ -52,7 +52,7 @@ function isBugReport({ labels, body } = {}) {
// Labels are authoritative: if any label was applied and it isn't `bug`,
// this is not a bug report (e.g. an `enhancement`-labeled issue that happens
// to contain a version heading must not be gated). Only fall back to the
// bug-template's `### GSD Version` heading for fully unlabeled submissions
// bug-template's `### MSD Version` heading for fully unlabeled submissions
// (bare REST API / `gh issue create`).
if (names.length > 0) return false;
return hasVersionHeading(body);
@@ -66,7 +66,7 @@ function hasVersionHeading(body) {
}
/**
* Extract the value beneath the "GSD Version" heading. Returns null when the
* Extract the value beneath the "MSD Version" heading. Returns null when the
* section is absent, '' when the section is present but empty.
*/
function extractVersion(body) {
@@ -111,7 +111,7 @@ function evaluateVersionGate({ labels, body } = {}) {
function renderCloseComment() {
return [
VERSION_GATE_MARKER,
'Closing automatically — this bug report does not include a valid **GSD Version**.',
'Closing automatically — this bug report does not include a valid **MSD Version**.',
'',
'A version is required to reproduce and triage bugs. Grab it with one of:',
'',

View File

@@ -14,7 +14,7 @@
/**
* Alias families the drift check validates. `router` is the module basename,
* shared by the `src/<router>.cts` source and the `gsd-core/bin/lib/<router>.cjs`
* shared by the `src/<router>.cts` source and the `msd-core/bin/lib/<router>.cjs`
* artifact it compiles to.
*/
const FAMILIES = Object.freeze([
@@ -34,7 +34,7 @@ const ALIAS_TABLE = 'command-aliases';
/**
* Repo-relative TRACKED sources whose edit should re-run the drift check.
*
* Deliberately the `src/*.cts` sources, never the `gsd-core/bin/lib/*.cjs`
* Deliberately the `src/*.cts` sources, never the `msd-core/bin/lib/*.cjs`
* build outputs they compile to: those are gitignored, so
* `git diff --cached --name-only` can never list them and a pre-commit guard
* keyed on them can never fire (#2725).

View File

@@ -5,7 +5,7 @@
//
// Why this copy exists: scripts/ runs straight from the repo checkout and must
// work on an unbuilt clone — 64+ scripts require this file, including
// check-env.cjs, which runs before any build. gsd-core/bin/lib/cli-exit.cjs is
// check-env.cjs, which runs before any build. msd-core/bin/lib/cli-exit.cjs is
// gitignored tsc output and doubles as the build sentinel, so it cannot be
// required from here. Hence one source, two emitted locations.
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
* json-error-mode and contract-version cells.
*
* Must import nothing but `node:fs` and `./exit-code-registry.cjs` — this
* source is emitted to TWO locations, gsd-core/bin/lib/cli-exit.cjs (tsc
* source is emitted to TWO locations, msd-core/bin/lib/cli-exit.cjs (tsc
* build output) and scripts/lib/cli-exit.cjs (a generated, committed
* artifact regenerated by scripts/gen-scripts-cli-exit.cjs), and the latter
* must load on an unbuilt clone before anything under ./lib exists. The
@@ -48,19 +48,19 @@ const exitCodeFor = (name) => exitCodeRegistryModule.exitCodeFor(name);
const EXIT_ENVELOPE_REASON = 'sdk_fail_fast';
/**
* Process-level flag: when true, error paths emit structured JSON to stderr
* instead of plain text. Set by gsd-tools.cjs when the CLI is invoked with
* instead of plain text. Set by msd-tools.cjs when the CLI is invoked with
* `--json-errors`; re-exported by io.cts, which is where most callers reach it.
*
* Held in a Symbol-keyed cell on globalThis rather than in module scope, and
* that is load-bearing: this module is emitted to TWO locations
* (gsd-core/bin/lib/cli-exit.cjs and the generated scripts/lib/cli-exit.cjs),
* (msd-core/bin/lib/cli-exit.cjs and the generated scripts/lib/cli-exit.cjs),
* so a process that loads both would get two independent module instances. A
* module-level `let` would give them two independent flags — one copy could
* think json mode is on while the other thought it was off, which is exactly
* the divergence class ADR-3889 exists to remove. One cell, keyed by a
* registry Symbol, makes that unrepresentable.
*/
const JSON_ERROR_MODE_KEY = Symbol.for('gsd.exit.jsonErrorMode');
const JSON_ERROR_MODE_KEY = Symbol.for('msd.exit.jsonErrorMode');
function setJsonErrorMode(v) {
globalThis[JSON_ERROR_MODE_KEY] = !!v;
}
@@ -80,7 +80,7 @@ const HOOK_DENY_CODE = exitCodeFor(HOOK_DENY_NAME);
* `resolveContractVersion` is the only writer; `terminateNow`/`runMain`
* read it internally when projecting a declared outcome.
*/
const CONTRACT_VERSION_KEY = Symbol.for('gsd.exit.contractVersion');
const CONTRACT_VERSION_KEY = Symbol.for('msd.exit.contractVersion');
function setContractVersion(v) {
globalThis[CONTRACT_VERSION_KEY] = v;
}
@@ -88,12 +88,12 @@ function setContractVersion(v) {
* Resolve the active exit-contract version, wiring the ambient process to the
* two terminators (ADR-3889 §4/§3). Mirrors how JSON_ERROR_MODE_KEY already
* works: a process-global cell means no entrypoint needs per-call wiring, so
* a `scripts/` tool or a hook gets the same behaviour as `gsd-tools` without
* this module touching either (P8 owns `gsd-tools`; P7 owns hooks).
* a `scripts/` tool or a hook gets the same behaviour as `msd-tools` without
* this module touching either (P8 owns `msd-tools`; P7 owns hooks).
*
* Precedence: if the cell already holds an explicit version, that wins —
* this is what lets `setContractVersion` override the ambient process (a
* later `GSD_EXIT_CONTRACT=v2` in the same process must NOT unseat an
* later `MSD_EXIT_CONTRACT=v2` in the same process must NOT unseat an
* explicit `setContractVersion('v1')` call). Otherwise resolve from argv/env
* via `resolveContractVersion`, which itself persists the result into the
* cell — so this is a one-time resolution per process; every later read is
@@ -176,7 +176,7 @@ function projectOutcome(outcome, version) {
* `main()` returning void would inherit a stale DEGRADED from an unrelated,
* earlier call — this is the leak #3912 review found and fixed.
*/
const PENDING_OUTCOME_KEY = Symbol.for('gsd.exit.pendingOutcome');
const PENDING_OUTCOME_KEY = Symbol.for('msd.exit.pendingOutcome');
function setPendingOutcome(v) {
globalThis[PENDING_OUTCOME_KEY] = v;
}
@@ -200,10 +200,10 @@ function findExitContractFlag(argv) {
* against it without re-parsing argv/env itself.
*
* Precedence: an explicit `--exit-contract=<v>` flag BEATS
* `GSD_EXIT_CONTRACT`, in both directions (flag=v1 + env=v2 -> v1; flag=v2 +
* `MSD_EXIT_CONTRACT`, in both directions (flag=v1 + env=v2 -> v1; flag=v2 +
* env=v1 -> v2). Neither present -> 'v1' (the documented default). An empty
* env var reads as UNSET, not as an explicit empty selection — a shell that
* exports `GSD_EXIT_CONTRACT=` with nothing after the `=` must not silently
* exports `MSD_EXIT_CONTRACT=` with nothing after the `=` must not silently
* select a version.
*
* Casing is decided, not accidental: only the exact lowercase tokens `v1`/
@@ -218,7 +218,7 @@ function resolveContractVersion(opts = {}) {
const argv = opts.argv ?? process.argv;
const env = opts.env ?? process.env;
const flagValue = findExitContractFlag(argv);
const rawEnvValue = env.GSD_EXIT_CONTRACT;
const rawEnvValue = env.MSD_EXIT_CONTRACT;
const envValue = rawEnvValue === undefined || rawEnvValue === '' ? undefined : rawEnvValue;
const selected = flagValue !== undefined ? flagValue : envValue;
let resolved;
@@ -404,7 +404,7 @@ function runMain(main) {
* fd 2 gets the SAME serialized `payload` fd 1 got — unchanged behavior.
* When provided, fd 2 gets THIS instead: a string is written raw
* (verbatim, not JSON-stringified), anything else is JSON-stringified
* like `payload`. This exists because `hooks/gsd-write-guard.js`'s
* like `payload`. This exists because `hooks/msd-write-guard.js`'s
* emitBlock does NOT write the same bytes to both streams today — it
* writes the full JSON `output` to stdout but only the plain-text
* `output.reason` STRING to stderr, because Kimi's native hook bus reads
@@ -454,7 +454,7 @@ function terminateNow(outcome, payload, stderrPayload) {
throw new Error(`terminateNow: exit code ${HOOK_DENY_CODE} is reserved to the ${HOOK_DENY_NAME} outcome; `
+ `got outcome ${JSON.stringify(outcome)}`);
}
// m2 (round 5, hooks/gsd-write-guard.js:159-175): emission must itself be
// m2 (round 5, hooks/msd-write-guard.js:159-175): emission must itself be
// exception-safe. A failed write (EPIPE, a full pipe buffer, a throwing
// fs.writeSync in a test) must NOT change the exit code — if it propagated
// out of this function, a caller whose payload could not be delivered
@@ -519,7 +519,7 @@ function terminateNow(outcome, payload, stderrPayload) {
process.exit(projected);
}
catch (err) {
// Anything above threw: an unrecognized --exit-contract/GSD_EXIT_CONTRACT
// Anything above threw: an unrecognized --exit-contract/MSD_EXIT_CONTRACT
// value, a non-string/empty/unregistered `outcome`, or the HOOK_DENY
// collision guard. Diagnose on stderr — swallowing this silently would
// make a typo'd outcome name or a bad contract-version env var

View File

@@ -10,7 +10,7 @@
* root-confinement / literal-tokenizer / sanitizer implementation instead of
* each copying it verbatim — the exact generative-fix-divergence class this
* epic exists to remove, now applied to the guards themselves (see
* `.gsd/phase/refactor-3184-milestone-window-single-owner/40-design.md`,
* `.msd/phase/refactor-3184-milestone-window-single-owner/40-design.md`,
* "Rejected: let the new drift guard copy Phase 1's tree-walk /
* root-confinement / sanitizer").
*

View File

@@ -1,9 +1,9 @@
'use strict';
// GENERATED FILE — DO NOT EDIT BY HAND.
// Source of truth: gsd-core/bin/shared/exit-codes.json. Regenerate with:
// Source of truth: msd-core/bin/shared/exit-codes.json. Regenerate with:
// node scripts/gen-exit-code-registry.cjs --write
// This exact content is emitted to THREE locations — gsd-core/bin/lib/exit-code-registry.cjs,
// This exact content is emitted to THREE locations — msd-core/bin/lib/exit-code-registry.cjs,
// scripts/lib/exit-code-registry.cjs, and hooks/lib/exit-code-registry.js (the latter two
// committed so scripts/ and hooks/ consumers work on an unbuilt clone) — all byte-compared by
// `npm run lint:generated-sync` (#3905 ADR-3889 Phase 1; #3906 Phase 2 added the second copy;
@@ -52,7 +52,7 @@ const EXIT_CODES = Object.freeze([
code: 80,
name: "DEGRADED",
meaning: "Ran to completion and is reporting a condition through its result payload rather than as a process failure",
owner: "gsd-tools",
owner: "msd-tools",
authorizedBy: "ADR-3889 + ADR-2980",
})
]);

View File

@@ -81,13 +81,6 @@ module.exports = {
"tests/git-base-branch.test.cjs",
"tests/graphify-query.test.cjs",
"tests/graphify-visualization.test.cjs",
"tests/gsd-secret-read-guard.test.cjs",
"tests/gsd-settings-advanced.test.cjs",
"tests/gsd-statusline.test.cjs",
"tests/gsd-tools-path-refs.test.cjs",
"tests/gsd-validate-commit-crash-policy.test.cjs",
"tests/gsd-validate-commit-sigpipe.test.cjs",
"tests/gsd-write-guard.test.cjs",
"tests/health-diagnostic-rules/worktree-health.test.cjs",
"tests/health-diagnostic.test.cjs",
"tests/helpers-cleanup.test.cjs",
@@ -107,7 +100,7 @@ module.exports = {
"tests/installer-migration-config-root-marker.test.cjs",
"tests/installer-migration-pi-retire-hooks-dir.test.cjs",
"tests/installer-migration-prune-stale-pristine.test.cjs",
"tests/installer-migration-rename-gsd-core.test.cjs",
"tests/installer-migration-rename-msd-core.test.cjs",
"tests/installer-migrations.test.cjs",
"tests/intel.test.cjs",
"tests/inventory-nested-families.test.cjs",
@@ -124,6 +117,13 @@ module.exports = {
"tests/milestone-window-single-owner.test.cjs",
"tests/model-catalog.unit.test.cjs",
"tests/model-resolver.test.cjs",
"tests/msd-secret-read-guard.test.cjs",
"tests/msd-settings-advanced.test.cjs",
"tests/msd-statusline.test.cjs",
"tests/msd-tools-path-refs.test.cjs",
"tests/msd-validate-commit-crash-policy.test.cjs",
"tests/msd-validate-commit-sigpipe.test.cjs",
"tests/msd-write-guard.test.cjs",
"tests/no-exact-case-env-access.rule.test.cjs",
"tests/no-pending-3212-markers.test.cjs",
"tests/no-phantom-issue-refs.test.cjs",

View File

@@ -9,7 +9,7 @@
// per-chunk timeout kills the child. This reporter runs ALONGSIDE the normal
// human reporter (a second `--test-reporter` on the same invocation, per
// Node's documented multi-reporter pairing) and appends one JSON object per
// line to a path supplied via the GSD_RUN_TESTS_EVENTS_FILE env var. On a
// line to a path supplied via the MSD_RUN_TESTS_EVENTS_FILE env var. On a
// timeout, run-tests.cjs reads that file back to name the file(s) still
// in flight (a `test:dequeue` with no matching `test:pass`/`test:fail`).
//
@@ -29,7 +29,7 @@
// sink. Node still requires a `--test-reporter-destination` to pair with
// this `--test-reporter` (see run-tests.cjs's reporterArgsFor), but that
// destination is a throwaway sink that stays empty by design — the durable
// path is GSD_RUN_TESTS_EVENTS_FILE, not the destination Node manages.
// path is MSD_RUN_TESTS_EVENTS_FILE, not the destination Node manages.
//
// Contract targeted: Node's "Custom reporters" contract
// (https://nodejs.org/api/test.html#custom-reporters) — a reporter module's
@@ -66,7 +66,7 @@
// interleave a partial write with the kill) never leaves more than one
// dangling unparsable trailing line.
module.exports = async function ndjsonEventReporter(source) {
const eventsPath = process.env.GSD_RUN_TESTS_EVENTS_FILE;
const eventsPath = process.env.MSD_RUN_TESTS_EVENTS_FILE;
// #3889: an init marker, written as this reporter's FIRST action — before
// the `for await` loop even begins consuming the event stream — so the
// events file's mere existence (and its exact contents) can distinguish

View File

@@ -17,7 +17,7 @@
* src/shell-command-projection.cts) computes it: `error.code ===
* 'ETIMEDOUT'`, which Node's spawnSync guarantees when its own `timeout`
* option fires. NOT imported directly here -- check-env.cjs deliberately
* cannot depend on that seam's compiled output (gsd-core/bin/lib/*.cjs), a
* cannot depend on that seam's compiled output (msd-core/bin/lib/*.cjs), a
* tsc build artifact that does not exist yet when check-env.cjs runs as its
* own standalone pre-`npm ci` CI step (confirmed live: an earlier version
* of this fix routed through execNpm directly and crashed every real CI

View File

@@ -103,14 +103,6 @@ module.exports = {
"tests/graphify-graph-path.test.cjs",
"tests/graphify-visualization.test.cjs",
"tests/graphify.test.cjs",
"tests/gsd-agent-isolation-guard.test.cjs",
"tests/gsd-check-update-worker-atomic-cache.test.cjs",
"tests/gsd-check-update-worker-platform-gate.test.cjs",
"tests/gsd-mcp-server-bin.test.cjs",
"tests/gsd-statusline.test.cjs",
"tests/gsd-validate-commit-crash-policy.test.cjs",
"tests/gsd-validate-commit-sigpipe.test.cjs",
"tests/gsd-write-guard.test.cjs",
"tests/health-diagnostic-rules/config-validation.test.cjs",
"tests/health-diagnostic-rules/worktree-health.test.cjs",
"tests/health-diagnostic.test.cjs",
@@ -134,7 +126,7 @@ module.exports = {
"tests/installer-migration-config-root-marker.test.cjs",
"tests/installer-migration-pi-retire-hooks-dir.test.cjs",
"tests/installer-migration-prune-stale-pristine.test.cjs",
"tests/installer-migration-rename-gsd-core.test.cjs",
"tests/installer-migration-rename-msd-core.test.cjs",
"tests/installer-migrations.test.cjs",
"tests/intel.test.cjs",
"tests/inventory-nested-families.test.cjs",
@@ -156,6 +148,14 @@ module.exports = {
"tests/milestone-archive.test.cjs",
"tests/milestone-window-single-owner.test.cjs",
"tests/model-resolver.test.cjs",
"tests/msd-agent-isolation-guard.test.cjs",
"tests/msd-check-update-worker-atomic-cache.test.cjs",
"tests/msd-check-update-worker-platform-gate.test.cjs",
"tests/msd-mcp-server-bin.test.cjs",
"tests/msd-statusline.test.cjs",
"tests/msd-validate-commit-crash-policy.test.cjs",
"tests/msd-validate-commit-sigpipe.test.cjs",
"tests/msd-write-guard.test.cjs",
"tests/mutation-workflow-base-ref.test.cjs",
"tests/new-milestone-clear-phases.test.cjs",
"tests/no-bare-npm-exec.rule.test.cjs",

View File

@@ -89,7 +89,7 @@ function httpsGetFollowingRedirects(url, redirectsLeft = MAX_REDIRECTS, requestF
return new Promise((resolve, reject) => {
const req = requestFn(
url,
{ headers: { 'User-Agent': 'gsd-core-shellcheck-fetch' }, timeout: DOWNLOAD_TIMEOUT_MS },
{ headers: { 'User-Agent': 'msd-core-shellcheck-fetch' }, timeout: DOWNLOAD_TIMEOUT_MS },
(res) => {
const status = res.statusCode || 0;
if (status >= 300 && status < 400 && res.headers.location) {

View File

@@ -51,9 +51,9 @@
"tests/frontmatter-cli.test.cjs :: source-text-is-the-product",
"tests/gates-taxonomy.test.cjs :: source-text-is-the-product",
"tests/git-base-branch.test.cjs :: source-text-is-the-product",
"tests/gsd-researcher-app-aware.test.cjs :: source-text-is-the-product",
"tests/gsd-researcher-flow-diagram.test.cjs :: source-text-is-the-product",
"tests/gsd-settings-advanced.test.cjs :: source-text-is-the-product",
"tests/msd-researcher-app-aware.test.cjs :: source-text-is-the-product",
"tests/msd-researcher-flow-diagram.test.cjs :: source-text-is-the-product",
"tests/msd-settings-advanced.test.cjs :: source-text-is-the-product",
"tests/helpers/live-command-registry.cjs :: source-text-is-the-product",
"tests/hermes-skills-migration.test.cjs :: source-text-is-the-product",
"tests/import-command.test.cjs :: source-text-is-the-product",
@@ -71,7 +71,7 @@
"tests/model-catalog-runtime-defaults.test.cjs :: source-text-is-the-product",
"tests/next-safety-gates.test.cjs :: source-text-is-the-product",
"tests/next-up-clear-order.test.cjs :: source-text-is-the-product",
"tests/no-hardcoded-home-gsd-tools.test.cjs :: source-text-is-the-product",
"tests/no-hardcoded-home-msd-tools.test.cjs :: source-text-is-the-product",
"tests/package-legitimacy-gate.test.cjs :: source-text-is-the-product",
"tests/parallel-dependent-plans.test.cjs :: source-text-is-the-product",
"tests/path-replacement.test.cjs :: source-text-is-the-product",

View File

@@ -77,7 +77,7 @@
* `local/no-source-grep` is registered by `eslint.config.mjs` on several
* glob blocks, not only `tests/** /*.cjs`: also `scripts/** /*.cjs`,
* `eslint-rules/** /*.cjs`, `bin/lib/** /*.cjs`, `pi/** /*.cjs`,
* `examples/** /*.cjs`, `gsd-core/bin/** /*.cjs`, `vscode/*.js`,
* `examples/** /*.cjs`, `msd-core/bin/** /*.cjs`, `vscode/*.js`,
* `.kilo/plugins/*.js`, and `.opencode/plugins/*.js`. A prior version of
* this script only walked `tests/** /*.test.cjs`, which silently dropped
* every non-`.test.cjs` file under `tests/` AND every file under every one
@@ -166,24 +166,24 @@ const noSourceGrepRule = require('../eslint-rules/no-source-grep.cjs');
const ROOT = path.join(__dirname, '..');
const ESLINT_CONFIG_PATH = path.join(ROOT, 'eslint.config.mjs');
const TESTS_DIR = process.env.GSD_LINT_ALLOW_TEST_RULE_TESTS_DIR || path.join(ROOT, 'tests');
const TESTS_DIR = process.env.MSD_LINT_ALLOW_TEST_RULE_TESTS_DIR || path.join(ROOT, 'tests');
// Overrides the ROOT that non-`tests/` glob base directories (scripts/,
// eslint-rules/, bin/lib/, pi/, examples/, gsd-core/bin/, vscode/,
// eslint-rules/, bin/lib/, pi/, examples/, msd-core/bin/, vscode/,
// .kilo/plugins/, .opencode/plugins/ — see deriveNoSourceGrepGlobs) are
// resolved under. TESTS_DIR already has its own override (above) for the
// `tests/**/*.cjs` block; this is the equivalent knob for every OTHER block
// that registers `local/no-source-grep`, so sandbox fixtures can exercise
// the widened scan scope (#3464 phase 5 BLOCKER fix) without the test suite
// re-scanning this repo's real scripts/eslint-rules/etc. trees on every run.
const EXTRA_ROOT = process.env.GSD_LINT_ALLOW_TEST_RULE_EXTRA_ROOT || ROOT;
const EXTRA_ROOT = process.env.MSD_LINT_ALLOW_TEST_RULE_EXTRA_ROOT || ROOT;
const ALLOWLIST_PATH =
process.env.GSD_LINT_ALLOW_TEST_RULE_ALLOWLIST ||
process.env.MSD_LINT_ALLOW_TEST_RULE_ALLOWLIST ||
path.join(__dirname, 'lint-allow-test-rule-refs.allowlist.json');
const EFFECTIVE_CEILING_PATH =
process.env.GSD_LINT_ALLOW_TEST_RULE_EFFECTIVE_CEILING ||
process.env.MSD_LINT_ALLOW_TEST_RULE_EFFECTIVE_CEILING ||
path.join(__dirname, 'lint-allow-test-rule-refs.effective-ceiling.json');
const UNVERIFIED_CEILING_PATH =
process.env.GSD_LINT_ALLOW_TEST_RULE_UNVERIFIED_CEILING ||
process.env.MSD_LINT_ALLOW_TEST_RULE_UNVERIFIED_CEILING ||
path.join(__dirname, 'lint-allow-test-rule-refs.unverified-ceiling.json');
/** Matches a compliant issue reference or URL */
@@ -247,7 +247,7 @@ function extractGenuineMarkerReasons(allComments) {
* (`tests/helpers/**`, `tests/qa/**`, `tests/fixtures/**`, ...) AND every one
* of the other config blocks the rule is registered on (`scripts/** /*.cjs`,
* `eslint-rules/** /*.cjs`, `bin/lib/** /*.cjs`, `pi/** /*.cjs`,
* `examples/** /*.cjs`, `gsd-core/bin/** /*.cjs`, `vscode/*.js`,
* `examples/** /*.cjs`, `msd-core/bin/** /*.cjs`, `vscode/*.js`,
* `.kilo/plugins/*.js`, `.opencode/plugins/*.js`). A marker in any of those
* would vanish from BOTH reported numbers instead of tripping anything.
*
@@ -365,9 +365,9 @@ function globToRegExp(glob) {
*
* Each glob's literal base directory (`globBaseDir`) is resolved to an
* actual directory on disk: the `tests` base uses `testsDir` (the existing
* `GSD_LINT_ALLOW_TEST_RULE_TESTS_DIR` override contract, unchanged), every
* `MSD_LINT_ALLOW_TEST_RULE_TESTS_DIR` override contract, unchanged), every
* other base is resolved under `extraRoot` (defaults to `ROOT`, overridable
* via `GSD_LINT_ALLOW_TEST_RULE_EXTRA_ROOT` so sandbox fixtures can exercise
* via `MSD_LINT_ALLOW_TEST_RULE_EXTRA_ROOT` so sandbox fixtures can exercise
* the widened scan scope in isolation). Matching itself is always done
* against the glob's own repo-relative path string (e.g. `tests/foo.cjs`,
* `scripts/bar.cjs`), never against the physical scan location, so a

View File

@@ -8,7 +8,7 @@
* ## Why
*
* `gen-plugin-skills.cjs --check` already guarantees `skills/*` /SKILL.md`
* matches byte-for-byte what `commands/gsd/*.md` generates, so the two trees
* matches byte-for-byte what `commands/msd/*.md` generates, so the two trees
* cannot silently diverge FROM EACH OTHER. Nothing guarded the shape #3085
* actually found: a command shipping `Bash` without `Grep` purely by
* omission, identical in both trees and therefore invisible to a parity
@@ -43,7 +43,7 @@ const path = require('path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.resolve(__dirname, '..');
const DEFAULT_ROOT = 'commands/gsd';
const DEFAULT_ROOT = 'commands/msd';
/**
* Commands allowed to declare `Bash` without `Grep`.
@@ -51,8 +51,8 @@ const DEFAULT_ROOT = 'commands/gsd';
* Keyed by command stem (the filename without `.md`), with the reason inline
* so changing the set is a one-line, reviewable diff.
*
* #4394 named eight candidates from the #3085 review: the six `gsd-ns-*`
* namespace dispatchers, `gsd-help`, and `gsd-surface`. Seven of those turn
* #4394 named eight candidates from the #3085 review: the six `msd-ns-*`
* namespace dispatchers, `msd-help`, and `msd-surface`. Seven of those turn
* out not to need an entry at all — they do not declare `Bash` in the first
* place, so the rule never reaches them:
*
@@ -187,7 +187,7 @@ function scan(root = DEFAULT_ROOT) {
}
function main() {
const root = process.env.GSD_LINT_ALLOWED_TOOLS_ROOT || DEFAULT_ROOT;
const root = process.env.MSD_LINT_ALLOWED_TOOLS_ROOT || DEFAULT_ROOT;
const { scanned, violations, staleExemptions } = scan(root);
if (violations.length > 0 || staleExemptions.length > 0) {

View File

@@ -2,15 +2,15 @@
/**
* lint-command-contract.cjs (ADR-0002)
*
* Enforces the commands/gsd/*.md contract across all 65 command files:
* Enforces the commands/msd/*.md contract across all 65 command files:
*
* 1. name: present, non-empty, matches gsd: or gsd- prefix
* 1. name: present, non-empty, matches msd: or msd- prefix
* 2. description: present, non-empty
* 3. allowed-tools: block present, non-empty, all entries from CANONICAL_TOOLS
* 4. execution_context @-refs: every @-reference resolves to an existing file on disk
* 5. execution_context @-refs: each appears on its own line (no trailing prose)
* 6. every gsd-core/workflows/*.md file is reachable from at least one
* commands/agents/skills loader, transitively through gsd-core/**
* 6. every msd-core/workflows/*.md file is reachable from at least one
* commands/agents/skills loader, transitively through msd-core/**
* (repo-level check, runs once — not per command file)
*
* Exit 0 = clean. Exit 1 = violations (with diagnostics).
@@ -32,8 +32,8 @@ function resolveRoot(argv) {
}
const ROOT = resolveRoot(process.argv.slice(2));
const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd');
const GSD_ROOT = path.join(ROOT, 'gsd-core');
const COMMANDS_DIR = path.join(ROOT, 'commands', 'msd');
const MSD_ROOT = path.join(ROOT, 'msd-core');
const {
CANONICAL_TOOLS,
@@ -64,8 +64,8 @@ function walkMarkdownFiles(dir, acc) {
return acc;
}
function toGsdRelative(absPath) {
return path.relative(GSD_ROOT, absPath).split(path.sep).join('/');
function toMsdRelative(absPath) {
return path.relative(MSD_ROOT, absPath).split(path.sep).join('/');
}
function checkWorkflowReachability() {
@@ -76,16 +76,16 @@ function checkWorkflowReachability() {
];
const loaderContents = loaderFiles.map(f => fs.readFileSync(f, 'utf-8'));
const gsdFileAbsPaths = walkMarkdownFiles(GSD_ROOT, []);
const gsdFiles = new Map();
for (const abs of gsdFileAbsPaths) {
gsdFiles.set(toGsdRelative(abs), fs.readFileSync(abs, 'utf-8'));
const msdFileAbsPaths = walkMarkdownFiles(MSD_ROOT, []);
const msdFiles = new Map();
for (const abs of msdFileAbsPaths) {
msdFiles.set(toMsdRelative(abs), fs.readFileSync(abs, 'utf-8'));
}
const workflowAbsPaths = walkMarkdownFiles(path.join(GSD_ROOT, 'workflows'), []);
const workflowPaths = workflowAbsPaths.map(toGsdRelative);
const workflowAbsPaths = walkMarkdownFiles(path.join(MSD_ROOT, 'workflows'), []);
const workflowPaths = workflowAbsPaths.map(toMsdRelative);
const unreachable = unreachableWorkflows(loaderContents, gsdFiles, workflowPaths);
const unreachable = unreachableWorkflows(loaderContents, msdFiles, workflowPaths);
return { workflowCount: workflowPaths.length, unreachable };
}
@@ -98,11 +98,11 @@ function check(filePath) {
const fm = parseFrontmatter(content);
const violations = [];
// 1. name: present + gsd: / gsd- prefix
// 1. name: present + msd: / msd- prefix
if (!fm.name || !fm.name.trim()) {
violations.push('name: field missing or empty');
} else if (!/^gsd[:-]/.test(fm.name.trim())) {
violations.push(`name: must start with "gsd:" or "gsd-", got "${fm.name.trim()}"`);
} else if (!/^msd[:-]/.test(fm.name.trim())) {
violations.push(`name: must start with "msd:" or "msd-", got "${fm.name.trim()}"`);
}
// 2. description: present + non-empty
@@ -126,7 +126,7 @@ function check(filePath) {
// 4+5. execution_context @-refs resolve + no trailing prose
const refs = extractExecutionContextRefs(content);
for (const { token, normalized, trailingProse } of refs) {
const absPath = path.join(GSD_ROOT, normalized);
const absPath = path.join(MSD_ROOT, normalized);
if (!fs.existsSync(absPath)) {
violations.push(`execution_context: @-ref "${normalized}" does not exist on disk`);
}
@@ -182,7 +182,7 @@ function main() {
`\nERROR lint-command-contract: ${unreachable.length} unreachable workflow file(s) of ${workflowCount}\n\n`,
);
for (const p of unreachable) {
process.stderr.write(` gsd-core/${p}\n`);
process.stderr.write(` msd-core/${p}\n`);
}
process.stderr.write(
'\nEach file above ships to every runtime but is never referenced by any command,\n' +

View File

@@ -1,16 +1,16 @@
#!/usr/bin/env node
/**
* lint-compiled-artifact-sync — fail when a *tracked* compiled artifact under
* gsd-core/bin/lib/ has drifted from its src/*.cts source.
* msd-core/bin/lib/ has drifted from its src/*.cts source.
*
* ADR-457 compiles src/*.cts to gsd-core/bin/lib/*.cjs at build time and expects
* ADR-457 compiles src/*.cts to msd-core/bin/lib/*.cjs at build time and expects
* those artifacts to be gitignored. Most are (see .gitignore). A handful are
* still tracked because the migration that moved the module into src/ did not
* also add the emitted .cjs to .gitignore. While a compiled artifact remains
* tracked, the committed bytes are what ships to anyone who reads the repo
* without building — so they must match the source.
*
* #2653: gsd-core/bin/lib/api-coverage.cjs sat four days behind its .cts after
* #2653: msd-core/bin/lib/api-coverage.cjs sat four days behind its .cts after
* PR #2551 changed the source without regenerating the artifact, shipping a
* module that silently lacked the entire #2366 fix while CI stayed green.
*
@@ -31,7 +31,7 @@ const os = require('node:os');
const path = require('node:path');
const REPO_ROOT = path.resolve(__dirname, '..');
const LIB_DIR = path.join('gsd-core', 'bin', 'lib');
const LIB_DIR = path.join('msd-core', 'bin', 'lib');
const SRC_DIR = 'src';
/** Frozen reason codes so tests assert on structure, not prose. */
@@ -52,7 +52,7 @@ function git(args) {
}
/**
* Tracked .cjs files under gsd-core/bin/lib/ that have a matching src/*.cts.
* Tracked .cjs files under msd-core/bin/lib/ that have a matching src/*.cts.
* Uses `git ls-files` so the set is derived from what git actually tracks
* rather than a hand-maintained list that could itself drift.
*/
@@ -70,7 +70,7 @@ function trackedCompiledArtifacts() {
/** Compile the whole project to a throwaway outDir so the work tree is untouched. */
function compileToTemp() {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-artifact-sync-'));
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-artifact-sync-'));
try {
execFileSync(
process.execPath,

View File

@@ -26,7 +26,7 @@
* WHOLE `src/` TREE, never an allowlist of known files. Per Decision 4(d)
* that surface is widened further: `src/` alone is itself the forbidden
* allowlist, one directory wide, so this guard ALSO scans the prompt-layer
* markdown (`gsd-core/workflows`, `commands`, `agents`, `skills`) — see the
* markdown (`msd-core/workflows`, `commands`, `agents`, `skills`) — see the
* "PROMPT-LAYER PROSE DETECTION" section below.
*
* FOUR INDEPENDENT SHAPES are detected, matching issue #3186's dispatch (shape
@@ -159,7 +159,7 @@
const path = require('node:path');
const driftScan = require('./lib/drift-scan.cjs');
const { MAX_REGEX_LITERAL_LEN, sanitizeForReport, scanTree } = driftScan;
const { escapeRegex } = require('../gsd-core/bin/lib/pattern.cjs');
const { escapeRegex } = require('../msd-core/bin/lib/pattern.cjs');
// ─── SHARED TOKENIZER + FUNCTION ATTRIBUTION (mirrors lint-state-field-drift.cjs) ──
//
@@ -731,7 +731,7 @@ function findCompletionPredicateDrift(text, relPath) {
// ─── PROMPT-LAYER PROSE/SHELL DETECTION (ADR-3180 Decision 4(d)) ───────────
//
// The SAME question — "is phase P complete?" — expressed as shell/jq inside
// workflow markdown rather than TypeScript. `gsd-core/workflows/mvp-phase.md`
// workflow markdown rather than TypeScript. `msd-core/workflows/mvp-phase.md`
// reads `.roadmap_complete` (a ROADMAP-checkbox-derived JSON field) into a
// shell variable and later OR's it directly into a `STATUS="completed"`
// decision — shape (a), independently of and in addition to
@@ -777,7 +777,7 @@ function findPromptCompletionDrift(text, _relPath) {
// Authored TypeScript source AND the prompt layer (ADR-3180 Decision 4(d)):
// `src/` alone is itself the forbidden allowlist Decision 4(d) names.
const SCAN_DIRS = ['src', 'gsd-core/workflows', 'commands', 'agents', 'skills'];
const SCAN_DIRS = ['src', 'msd-core/workflows', 'commands', 'agents', 'skills'];
const SCAN_EXT = new Set(['.cts', '.ts', '.mts', '.md']);
// The designated owner (issue #3186, ADR-3180 §7.4) — does NOT exist yet.

View File

@@ -16,7 +16,7 @@
*
* The DEFECT text names two surfaces: `CONFIG_DEFAULTS` and
* `buildNewProjectConfig`. This check covers ONLY the single-source-of-truth
* defaults manifest, `gsd-core/bin/shared/config-defaults.manifest.json`
* defaults manifest, `msd-core/bin/shared/config-defaults.manifest.json`
* (what `CONFIG_DEFAULTS` in `src/configuration.cts` / `src/config.cts`
* actually loads at runtime) — because it is pure JSON, a resolved
* key→value-map diff between base and head is trivially reliable: no line
@@ -53,7 +53,7 @@ const cp = require('node:child_process');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.join(__dirname, '..');
const MANIFEST_PATH = path.join('gsd-core', 'bin', 'shared', 'config-defaults.manifest.json');
const MANIFEST_PATH = path.join('msd-core', 'bin', 'shared', 'config-defaults.manifest.json');
const BREAKING_CHANGES_RE = /^#{1,6}\s*Breaking Changes\b/im;
/**
@@ -170,7 +170,7 @@ function main() {
+ 'config-defaults.manifest.json (DEFECT.DEFAULT-FLIP-DOCUMENTATION) but the PR body has no\n'
+ '`## Breaking Changes` section. Add one covering: (a) when the new default takes effect\n'
+ '(config-set, fresh project, regenerated config), (b) the opt-back-in command\n'
+ '(`gsd config-set <key> <old-value>`), (c) effect on in-flight artifacts. Changed default(s):\n'
+ '(`msd config-set <key> <old-value>`), (c) effect on in-flight artifacts. Changed default(s):\n'
+ detail,
);
}

View File

@@ -2,12 +2,12 @@
/**
* lint-descriptions.cjs
*
* Enforces the 100-char description budget for commands/gsd/*.md files.
* Enforces the 100-char description budget for commands/msd/*.md files.
*
* Usage:
* node scripts/lint-descriptions.cjs [file.md ...]
*
* If no args are given, scans commands/gsd/ automatically.
* If no args are given, scans commands/msd/ automatically.
* Exits 1 if any description exceeds 100 chars; exits 0 if all pass.
*/
@@ -18,7 +18,7 @@ const path = require('path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const MAX_LENGTH = 100;
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'msd');
/**
* Parse the description field from frontmatter in a .md file.

View File

@@ -4,40 +4,40 @@
*
* Enforces the human-facing command form in docs/ (#2903).
*
* `/gsd:<cmd>` (and bare `gsd:<cmd>`) is a SOURCE-AUTHORING token: install-time
* `/msd:<cmd>` (and bare `msd:<cmd>`) is a SOURCE-AUTHORING token: install-time
* converters (`transformContentToHyphen`, `convertSlashCommandsTo<Runtime>SkillMentions`)
* rewrite it to `/gsd-<cmd>` per-runtime. It is correct in `commands/gsd/**`,
* `gsd-core/workflows/**`, and `agents/**` — but docs are never passed through a
* converter, so a doc telling a reader to type `/gsd:<cmd>` names a command no
* runtime registers. The real user-facing form is `/gsd-<cmd>`.
* rewrite it to `/msd-<cmd>` per-runtime. It is correct in `commands/msd/**`,
* `msd-core/workflows/**`, and `agents/**` — but docs are never passed through a
* converter, so a doc telling a reader to type `/msd:<cmd>` names a command no
* runtime registers. The real user-facing form is `/msd-<cmd>`.
*
* This guard fails any `docs/**\/*.md` file that still contains `/gsd:<cmd>` or
* bare `gsd:<cmd>` where `<cmd>` is a real command name (drawn from the
* `commands/gsd/*.md` roster). It explicitly permits `/gsd-core:<cmd>` (the
* Claude Code plugin namespace) and any `gsd:<token>` whose `<token>` is not a
* real command (e.g. the `gsd:section` / `gsd:loop-host` workflow-fragment
* This guard fails any `docs/**\/*.md` file that still contains `/msd:<cmd>` or
* bare `msd:<cmd>` where `<cmd>` is a real command name (drawn from the
* `commands/msd/*.md` roster). It explicitly permits `/msd-core:<cmd>` (the
* Claude Code plugin namespace) and any `msd:<token>` whose `<token>` is not a
* real command (e.g. the `msd:section` / `msd:loop-host` workflow-fragment
* marker syntax documented in docs/reference/workflow-fragments.md, or
* `gsd:command-name` used as a placeholder while explaining the Gemini CLI
* `msd:command-name` used as a placeholder while explaining the Gemini CLI
* colon-form convention).
*
* Exclusions (never checked):
* - docs/adr/** (historical record)
* - docs/RELEASE-NOTES-LEGACY.md (maintainer question still open)
* - commands/gsd/**, gsd-core/workflows/**, agents/** — never touched by this
* - commands/msd/**, msd-core/workflows/**, agents/** — never touched by this
* guard at all; the colon form is correct there.
*
* Exemption — `name:` frontmatter key citations: source command files
* (`commands/gsd/*.md`) carry the colon form in their `name:` YAML frontmatter
* key (e.g. `name: gsd:next`). A doc that quotes that key verbatim — e.g.
* `` `name: gsd:next` `` or `name: gsd:next` — is citing the real source file,
* not telling a reader what to type. Rewriting that citation to `gsd-next`
* would make the doc lie about the source it's quoting, so a `gsd:<cmd>` token
* (`commands/msd/*.md`) carry the colon form in their `name:` YAML frontmatter
* key (e.g. `name: msd:next`). A doc that quotes that key verbatim — e.g.
* `` `name: msd:next` `` or `name: msd:next` — is citing the real source file,
* not telling a reader what to type. Rewriting that citation to `msd-next`
* would make the doc lie about the source it's quoting, so a `msd:<cmd>` token
* immediately preceded by `name:` (optionally with a backtick/whitespace in
* between) is permitted. This is narrow: it does not exempt `gsd:<cmd>`
* anywhere else on the line or file, including the reader-facing `/gsd-<cmd>`
* between) is permitted. This is narrow: it does not exempt `msd:<cmd>`
* anywhere else on the line or file, including the reader-facing `/msd-<cmd>`
* form that may appear later in the same sentence.
*
* Detection is case-insensitive (`/GSD:next`, `Gsd:Next`, etc. are all
* Detection is case-insensitive (`/MSD:next`, `Msd:Next`, etc. are all
* flagged) since the install-time converters and runtimes treat command names
* case-insensitively in practice, and a doc typo in casing is still a lie
* about the real command form.
@@ -53,23 +53,23 @@ const path = require('path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const SELF_PATH = path.resolve(__filename);
// GSD_LINT_DOCS_COMMAND_FORM_REPO_ROOT is used by tests to redirect the guard to
// MSD_LINT_DOCS_COMMAND_FORM_REPO_ROOT is used by tests to redirect the guard to
// a temporary fixture git repo without touching the real working tree.
const REPO_ROOT = process.env.GSD_LINT_DOCS_COMMAND_FORM_REPO_ROOT
? path.resolve(process.env.GSD_LINT_DOCS_COMMAND_FORM_REPO_ROOT)
const REPO_ROOT = process.env.MSD_LINT_DOCS_COMMAND_FORM_REPO_ROOT
? path.resolve(process.env.MSD_LINT_DOCS_COMMAND_FORM_REPO_ROOT)
: path.resolve(__dirname, '..');
const DOCS_PREFIX = 'docs/';
const ADR_PREFIX = 'docs/adr/';
const RELEASE_NOTES_LEGACY = 'docs/RELEASE-NOTES-LEGACY.md';
const COMMANDS_DIR = path.join(REPO_ROOT, 'commands/gsd');
const COMMANDS_DIR = path.join(REPO_ROOT, 'commands/msd');
// Matches `/gsd:<cmd>` and bare `gsd:<cmd>` (not part of `/gsd-core:<cmd>`,
// which does not contain the substring `gsd:` — the hyphen breaks it).
// Case-insensitive so `/GSD:next` / `Gsd:Next` are also caught.
const COMMAND_FORM_RE = /(^|[^A-Za-z0-9_-])(\/)?gsd:([A-Za-z0-9_-]+)/gi;
// Matches `/msd:<cmd>` and bare `msd:<cmd>` (not part of `/msd-core:<cmd>`,
// which does not contain the substring `msd:` — the hyphen breaks it).
// Case-insensitive so `/MSD:next` / `Msd:Next` are also caught.
const COMMAND_FORM_RE = /(^|[^A-Za-z0-9_-])(\/)?msd:([A-Za-z0-9_-]+)/gi;
// A `gsd:<cmd>` token is exempt when it is a citation of a source file's
// A `msd:<cmd>` token is exempt when it is a citation of a source file's
// YAML `name:` frontmatter key — i.e. the text immediately before the match
// (ending exactly where the match begins) is `name:` followed by optional
// whitespace and/or a backtick. Only applies to the bare (non-`/`) form,
@@ -81,7 +81,7 @@ function loadRoster() {
try {
entries = fs.readdirSync(COMMANDS_DIR);
} catch (err) {
throw new ExitError(1, 'ERROR lint-docs-command-form: could not read commands/gsd: ' + err.message);
throw new ExitError(1, 'ERROR lint-docs-command-form: could not read commands/msd: ' + err.message);
}
return new Set(
entries.filter((f) => f.endsWith('.md')).map((f) => f.replace(/\.md$/, '')),
@@ -117,7 +117,7 @@ function scanContent(relPath, content, roster) {
const preContext = line.slice(0, match.index + pre.length);
if (NAME_KEY_CITATION_RE.test(preContext)) continue;
}
const matchedToken = (slash || '') + 'gsd:' + cmd;
const matchedToken = (slash || '') + 'msd:' + cmd;
violations.push({
file: relPath,
line: i + 1,
@@ -179,7 +179,7 @@ function main() {
' colon form names a command no runtime registers. Rewrite to the hyphen form\n',
);
process.stderr.write(
' (`/gsd-<cmd>`), or `/gsd-core:<cmd>` if this is genuinely the Claude Code\n',
' (`/msd-<cmd>`), or `/msd-core:<cmd>` if this is genuinely the Claude Code\n',
);
process.stderr.write(' plugin namespace.\n\n');
return 1;

View File

@@ -57,7 +57,7 @@ const DOCS_GUARD_EXEMPT_BASELINE = [
'estimate-calibrate.test.cjs',
'gen-context-index.test.cjs',
'gen-registry.test.cjs',
'gsd-agent-isolation-guard.test.cjs',
'msd-agent-isolation-guard.test.cjs',
'hermes-dispatch-upgrade.test.cjs',
'install-minimal-hooks.test.cjs',
'install-runtime-artifacts.test.cjs',
@@ -115,7 +115,7 @@ const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
'adr-parser.unit.test.cjs': ['docs/adr/0001.md', 'docs/adr/0099.md', 'docs/adr/NNNN.md'],
'agent-marker-documentation-guard.test.cjs': ['docs/reference', 'docs/reference/workflow-fragments.md'],
'antigravity-upgrades.test.cjs': ['docs/cli', 'docs/cli/gcli-migration', 'docs/cli/permissions'],
'capability-cli.test.cjs': ['docs/reference/gsd-capability-command.md'],
'capability-cli.test.cjs': ['docs/reference/msd-capability-command.md'],
// #3970: cites docs/adr/3646-per-task-content-resolution-seam.md in an
// explanatory comment describing ADR-3646's Decision 3; the file never
// reads that (or any) docs/ file.
@@ -165,7 +165,7 @@ const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
],
'gen-context-index.test.cjs': ['docs/CONTEXT-INDEX.json', 'docs/INVENTORY-MANIFEST.json'],
'gen-registry.test.cjs': ['docs/registries', 'docs/registries/reviewers.json'],
'gsd-agent-isolation-guard.test.cjs': ['docs/adr/1239-...md', 'docs/adr/1239-gsd-embeddable-orchestration-engine.md'],
'msd-agent-isolation-guard.test.cjs': ['docs/adr/1239-...md', 'docs/adr/1239-msd-embeddable-orchestration-engine.md'],
'hermes-dispatch-upgrade.test.cjs': ['docs/guides/delegation-patterns.md'],
'install-minimal-hooks.test.cjs': ['docs/en/hooks', 'docs/en/users/features/hooks'],
'install-runtime-artifacts.test.cjs': ['docs/CONFIGURATION.md', 'docs/adr/58-...md', 'docs/adr/58-runtime-install-policy-module.md', 'docs/cli/slash-commands'],
@@ -217,9 +217,9 @@ const DOCS_GUARD_EXEMPT_DOCS_PATHS = {
'opencode-command-dir-plural.test.cjs': ['docs/commands'],
'phase.test.cjs': ['docs/adr/3524-...md', 'docs/adr/3524-cjs-sdk-hard-seam.md'],
'pr-branch-planning-filter.test.cjs': ['docs/readme.md'],
'precommit-alias-drift-hook.test.cjs': ['docs/adr/0174-...md', 'docs/adr/0174-retire-gsd-sdk-package-boundary.md'],
'precommit-alias-drift-hook.test.cjs': ['docs/adr/0174-...md', 'docs/adr/0174-retire-msd-sdk-package-boundary.md'],
'removed-but-needed-lint.test.cjs': [
'docs/README.md', 'docs/getting-started.md', 'docs/gsd-new-workspace.md', 'docs/setup.md', 'docs/some-doc.md',
'docs/README.md', 'docs/getting-started.md', 'docs/msd-new-workspace.md', 'docs/setup.md', 'docs/some-doc.md',
],
'repo-invariants.test.cjs': ['docs/FEATURES.md', 'docs/workflows/README'],
'require-issue-link-policy.test.cjs': ['docs/-prefixed', 'docs/CONFIGURATION.md', 'docs/a.md', 'docs/b.md', 'docs/guide.md'],

View File

@@ -1,6 +1,6 @@
[
{
"path": "gsd-core/bin/lib/vendor/re2js.d.cts",
"path": "msd-core/bin/lib/vendor/re2js.d.cts",
"reason": "Vendored third-party type declaration (#3477): a .d.cts carries no executable code, so no ESLint rule is meaningful; the vendored .cjs it describes is globally ignored as verbatim upstream output. Freshness is enforced by scripts/lint-vendored-deps.cjs, not by lint rules."
},
{
@@ -9,7 +9,7 @@
},
{
"path": "src/vendor/js-yaml.d.cts",
"reason": "Vendored third-party type declaration (ADR-3473 §8.1, #3881): a .d.cts carries no executable code, so no ESLint rule is meaningful; the vendored js-yaml .cjs it describes is globally ignored as verbatim upstream output (gsd-core/bin/lib/vendor/**). Freshness is enforced by scripts/lint-vendored-deps.cjs, not by lint rules — same precedent as src/vendor/re2js.d.cts above."
"reason": "Vendored third-party type declaration (ADR-3473 §8.1, #3881): a .d.cts carries no executable code, so no ESLint rule is meaningful; the vendored js-yaml .cjs it describes is globally ignored as verbatim upstream output (msd-core/bin/lib/vendor/**). Freshness is enforced by scripts/lint-vendored-deps.cjs, not by lint rules — same precedent as src/vendor/re2js.d.cts above."
},
{
"path": "tests/fixtures/brand-typing/bad-calibrated-as-sample-basis.cts",

View File

@@ -40,7 +40,7 @@
*
* ESLint's `ignores:` blocks are the one legitimate "this file is not meant
* to be linted" decision (e.g. the ADR-457 tsc-emitted `.cjs` artifacts
* under `gsd-core/bin/lib/`) and must NOT be reported as uncovered. But
* under `msd-core/bin/lib/`) and must NOT be reported as uncovered. But
* `ESLint#isPathIgnored` cannot be trusted uniformly across extensions:
* for ESLint's default-lintable extensions (`.js`/`.mjs`/`.cjs`), it
* reports `true` only when the path matches an explicit `ignores:` glob —
@@ -61,7 +61,7 @@
*
* The allowlist below is exclusively for files that resolve to ZERO rules —
* it is a registry of accepted escapes, not a general-purpose "reasons for
* how a file is configured" log. The `bin/install.js` / `bin/gsd-mcp-server.js`
* how a file is configured" log. The `bin/install.js` / `bin/msd-mcp-server.js`
* / `scripts/build-hooks.js` family is deliberately covered by a minimal,
* 2-rule block in `eslint.config.mjs` per ADR-1703 (targeting only the
* portability defect surface, not a full style sweep of ~12k lines of

View File

@@ -4,7 +4,7 @@
/**
* lint-example-parser-parity.cjs — asserts examples/dynamic-context-management/
* context-predicates.cjs (reference prototype, ADR-1671) and production's
* src/context-predicates.cts (compiled to gsd-core/bin/lib/context-predicates.cjs)
* src/context-predicates.cts (compiled to msd-core/bin/lib/context-predicates.cjs)
* agree on parsing behavior, and that the example's own committed
* CONTEXT-INDEX.json has not silently drifted from a fresh parse.
*
@@ -55,7 +55,7 @@ const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.resolve(__dirname, '..');
const PROD_PREDICATES_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'context-predicates.cjs');
const PROD_PREDICATES_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'context-predicates.cjs');
const EXAMPLE_DIR = path.join(ROOT, 'examples', 'dynamic-context-management');
const EXAMPLE_PREDICATES_PATH = path.join(EXAMPLE_DIR, 'context-predicates.cjs');
const DEFAULT_EXAMPLE_INDEX_PATH = path.join(EXAMPLE_DIR, 'CONTEXT-INDEX.json');
@@ -361,7 +361,7 @@ function main() {
for (const d of diagnostics) process.stderr.write(` - ${d}\n`);
process.stderr.write(
'\nexamples/dynamic-context-management/context-predicates.cjs and ' +
'src/context-predicates.cts (gsd-core/bin/lib/context-predicates.cjs) must agree; ' +
'src/context-predicates.cts (msd-core/bin/lib/context-predicates.cjs) must agree; ' +
'the committed examples/dynamic-context-management/CONTEXT-INDEX.json must match a ' +
'fresh parse of CONTEXT.md on every line-number-independent fact (run:\n' +
' node examples/dynamic-context-management/gen-context-index.cjs --write\n' +

View File

@@ -35,7 +35,7 @@
*
* ## Overrides
*
* Set GSD_SKIP_REGRESSION_TEST_GATE=1 for legitimate exceptions (e.g. a
* Set MSD_SKIP_REGRESSION_TEST_GATE=1 for legitimate exceptions (e.g. a
* pure-config or pure-refactor PR where no behavioral test is possible).
* This env var is auditable in CI logs.
*/
@@ -87,12 +87,12 @@ function getChangedTestFiles(baseRef) {
}
function main() {
if (process.env.GSD_SKIP_REGRESSION_TEST_GATE === '1') {
console.log('lint-fix-has-regression-tests: SKIPPED (GSD_SKIP_REGRESSION_TEST_GATE=1)');
if (process.env.MSD_SKIP_REGRESSION_TEST_GATE === '1') {
console.log('lint-fix-has-regression-tests: SKIPPED (MSD_SKIP_REGRESSION_TEST_GATE=1)');
return;
}
const baseRef = process.env.GSD_REGRESSION_GATE_BASE || 'origin/next';
const baseRef = process.env.MSD_REGRESSION_GATE_BASE || 'origin/next';
let fixCommits;
try {
@@ -124,7 +124,7 @@ function main() {
`Fix commits:\n${commitList}\n\n` +
`CONTRIBUTING.md:47: "Write a test that would have caught the bug."\n` +
`Add a regression test to an existing tests/*.test.cjs file, or set ` +
`GSD_SKIP_REGRESSION_TEST_GATE=1 if no behavioral test is possible (auditable in CI).`
`MSD_SKIP_REGRESSION_TEST_GATE=1 if no behavioral test is possible (auditable in CI).`
);
}

View File

@@ -23,7 +23,7 @@
*
* ## What this scans
*
* Every fenced ```bash / ```sh code block in `gsd-core/workflows/*.md`,
* Every fenced ```bash / ```sh code block in `msd-core/workflows/*.md`,
* `agents/*.md`, and `commands/**\/*.md`. Within each block, flags a
* `grep "^key:"` / `grep '^key:'` invocation that:
* - is NOT preceded (earlier in the SAME block) by a frontmatter-scoping
@@ -50,7 +50,7 @@ const path = require('path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.join(__dirname, '..');
const DEFAULT_ROOTS = ['gsd-core/workflows', 'agents', 'commands'];
const DEFAULT_ROOTS = ['msd-core/workflows', 'agents', 'commands'];
const FENCE_RE = /^```(bash|sh)\s*$/;
const FENCE_END_RE = /^```\s*$/;
@@ -213,7 +213,7 @@ function scan(roots = DEFAULT_ROOTS) {
}
function main() {
const rootsEnv = process.env.GSD_LINT_FRONTMATTER_SCALAR_ROOTS;
const rootsEnv = process.env.MSD_LINT_FRONTMATTER_SCALAR_ROOTS;
const roots = rootsEnv ? rootsEnv.split(path.delimiter).filter(Boolean) : DEFAULT_ROOTS;
const offenders = scan(roots);
if (offenders.length > 0) {

View File

@@ -9,7 +9,7 @@
* ## What this enforces
*
* 1. (§8.2 rule 1 — 1:1 code invariant) Every `rule.code` in RULES (exported
* from the compiled `gsd-core/bin/lib/health-diagnostic.cjs`) is unique,
* from the compiled `msd-core/bin/lib/health-diagnostic.cjs`) is unique,
* and every rule's `severity` is one of `SEVERITY`'s values. The severity
* check exists only to confirm the compiled artifact was not hand-edited
* to bypass the `Rule.severity` required field TypeScript already
@@ -45,7 +45,7 @@
* mirroring the C0NN pass's structure, just with a hardcoded list
* instead of a `Rule[]` array as the code source.
*
* Design: .gsd/phase/refactor-3309-health-diagnostic-rule-table/40-design.md
* Design: .msd/phase/refactor-3309-health-diagnostic-rule-table/40-design.md
* ("The lint guard (§8.2 1:1 invariant + §8.5 fixture proof)").
*
* ## Deviation from the design doc's original plan
@@ -68,7 +68,7 @@ const path = require('node:path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const REPO_ROOT = path.join(__dirname, '..');
const COMPILED_MODULE_REL = 'gsd-core/bin/lib/health-diagnostic.cjs';
const COMPILED_MODULE_REL = 'msd-core/bin/lib/health-diagnostic.cjs';
const COMPILED_MODULE_PATH = path.join(REPO_ROOT, COMPILED_MODULE_REL);
const TEST_GROUP_DIR = path.join(REPO_ROOT, 'tests', 'health-diagnostic-rules');
const SKELETON_TEST_FILE = path.join(REPO_ROOT, 'tests', 'health-diagnostic.test.cjs');

View File

@@ -3,16 +3,16 @@
/**
* lint-hooks-runtime-build-seam.cjs — every `hooks/**` file that requires a
* compiled runtime-library module (`gsd-core/bin/lib/*.cjs`) must also go
* through the self-healing build seam (`gsd-core/bin/ensure-runtime-build.cjs`,
* compiled runtime-library module (`msd-core/bin/lib/*.cjs`) must also go
* through the self-healing build seam (`msd-core/bin/ensure-runtime-build.cjs`,
* #2002) before that require can be reached (#3582).
*
* ## Why
*
* `gsd-core/bin/lib/*.cjs` are compiled from `src/*.cts` by `npm run
* `msd-core/bin/lib/*.cjs` are compiled from `src/*.cts` by `npm run
* build:lib` (ADR-457, "build-at-publish") and are gitignored. The npm
* package builds before publishing, so the artifacts exist on that path.
* `gsd-core/bin/gsd-tools.cjs` calls `ensureRuntimeBuild()` before requiring
* `msd-core/bin/msd-tools.cjs` calls `ensureRuntimeBuild()` before requiring
* its own `./lib` — but a plugin-marketplace / git-clone install that never
* runs `npm run build:lib` materializes the raw tree with the compiled
* `./lib` absent, and NO hook self-healed before this issue. A hook that
@@ -28,18 +28,18 @@
* For every `.js`/`.cjs` file under `hooks/` (excluding the generated
* `hooks/dist/` build-output copy, which is not source): if the file's code
* (comments stripped) contains a `require(...)` of a path that names
* `gsd-core/bin/lib/<name>.cjs` (any relative-path spelling — `../gsd-core/…`,
* `../../gsd-core/…`, etc. — the segment `gsd-core/bin/lib/` plus a trailing
* `msd-core/bin/lib/<name>.cjs` (any relative-path spelling — `../msd-core/…`,
* `../../msd-core/…`, etc. — the segment `msd-core/bin/lib/` plus a trailing
* `.cjs` is the discriminator, not the exact prefix), the SAME file must ALSO
* contain BOTH:
* 1. a `require(...)` of a path naming `gsd-core/bin/ensure-runtime-build.cjs`
* 1. a `require(...)` of a path naming `msd-core/bin/ensure-runtime-build.cjs`
* (the seam module itself), AND
* 2. an actual INVOCATION `ensureRuntimeBuild(` — not merely a destructuring
* import with no call, which would import the seam but never run it.
*
* File-level co-occurrence, not line-by-line ordering: this repo's hooks
* define helper functions in whatever order reads best and call them in a
* different order at runtime (e.g. `hooks/gsd-cursor-subagent-start.js`'s
* different order at runtime (e.g. `hooks/msd-cursor-subagent-start.js`'s
* `resolveFallbackIsolation` is defined textually ABOVE the single
* `ensureRuntimeBuild()` call site in `evaluateRootIsolation`, which is its
* only caller) — a strict "seam call must appear on an earlier LINE than the
@@ -53,17 +53,17 @@
*
* ## What PASSES
*
* - A hook that never requires `gsd-core/bin/lib/*.cjs` at all (most of
* - A hook that never requires `msd-core/bin/lib/*.cjs` at all (most of
* `hooks/`) — nothing to check.
* - A hook that requires a compiled module AND requires + calls
* `ensureRuntimeBuild()` somewhere in the same file.
* - A prose comment that mentions `gsd-core/bin/lib/…` without a real,
* - A prose comment that mentions `msd-core/bin/lib/…` without a real,
* well-formed `require('....cjs')` call (comments are stripped before
* scanning).
*
* ## What FAILS
*
* - A hook that requires a compiled `gsd-core/bin/lib/*.cjs` module but never
* - A hook that requires a compiled `msd-core/bin/lib/*.cjs` module but never
* requires `ensure-runtime-build.cjs`, or requires it but never calls
* `ensureRuntimeBuild(...)`.
*
@@ -71,14 +71,14 @@
*
* - **Literal-string matching only.** `REQUIRE_RE` matches `require(...)`
* called with a single- OR double-quoted string literal argument. A
* concatenated/computed path (`require(base + '/gsd-core/bin/lib/x.cjs')`),
* concatenated/computed path (`require(base + '/msd-core/bin/lib/x.cjs')`),
* a template literal (`` require(`${dir}/x.cjs`) ``), or `createRequire(...)`
* / `require.resolve` used indirectly all evade `isCompiledLibRequire` and
* `isSeamRequire` — none of them appear as a literal quoted `require(...)`
* call, so a file using one of these forms scans as "requires nothing",
* even if it genuinely needs the seam.
* - **`hooks/` only.** `scanRepo`'s `walk()` only descends `SCAN_ROOT`
* (`hooks/`, minus `hooks/dist/`). A compiled `gsd-core/bin/lib/*.cjs`
* (`hooks/`, minus `hooks/dist/`). A compiled `msd-core/bin/lib/*.cjs`
* require sitting inside a NON-hooks helper module that a hook file then
* requires (directly or transitively) is invisible to this scan — the scan
* only ever reads the hook file's OWN text, never follows its require
@@ -107,7 +107,7 @@ const EXCLUDE_DIR_NAMES = new Set(['dist']);
// Line-based comment stripper (deliberately NOT the naive two-regex
// `/\*[\s\S]*?\*\//g` then `//` approach other tests in this repo use for
// simpler files): this module's own source comments legitimately spell
// `gsd-core/bin/lib/*.cjs` (a glob) inside a `//` line, which forms a bare
// `msd-core/bin/lib/*.cjs` (a glob) inside a `//` line, which forms a bare
// `/*` token — a whole-text block-comment regex applied naively would read
// that as an OPENING block comment and swallow everything up to the next
// unrelated `*/` anywhere later in the file, silently deleting real code
@@ -158,11 +158,11 @@ function stripComments(text) {
const REQUIRE_RE = /require\(\s*(['"])([^'"]+)\1\s*\)/g;
function isCompiledLibRequire(requirePath) {
return requirePath.includes('gsd-core/bin/lib/') && requirePath.endsWith('.cjs');
return requirePath.includes('msd-core/bin/lib/') && requirePath.endsWith('.cjs');
}
function isSeamRequire(requirePath) {
return requirePath.endsWith('gsd-core/bin/ensure-runtime-build.cjs');
return requirePath.endsWith('msd-core/bin/ensure-runtime-build.cjs');
}
// An actual invocation, not merely a `{ ensureRuntimeBuild }` destructuring
@@ -224,7 +224,7 @@ function scanRepo(root) {
if (compiledLibRequires.length === 0) continue;
if (hasSeamRequire && hasSeamCall) continue;
const missing = [];
if (!hasSeamRequire) missing.push('require(".../gsd-core/bin/ensure-runtime-build.cjs")');
if (!hasSeamRequire) missing.push('require(".../msd-core/bin/ensure-runtime-build.cjs")');
if (!hasSeamCall) missing.push('a call to ensureRuntimeBuild(...)');
violations.push({
file: path.relative(root, full).split(path.sep).join('/'),
@@ -248,8 +248,8 @@ function main() {
throw new ExitError(
1,
`lint-hooks-runtime-build-seam: ${violations.length} hooks/ file(s) require a compiled\n` +
`gsd-core/bin/lib/*.cjs module without also self-healing via\n` +
`ensureRuntimeBuild() from gsd-core/bin/ensure-runtime-build.cjs first (#3582) — on a\n` +
`msd-core/bin/lib/*.cjs module without also self-healing via\n` +
`ensureRuntimeBuild() from msd-core/bin/ensure-runtime-build.cjs first (#3582) — on a\n` +
`plugin-marketplace/git-clone install that never ran \`npm run build:lib\`, that\n` +
`require crashes (or is silently misreported) instead of self-building:\n\n${detail}\n`,
);

View File

@@ -3,7 +3,7 @@
* lint-legacy-dir-name.cjs
*
* Prevents accidental re-introduction of the bare legacy directory token
* `get-shit-done` now that the package has been renamed to `gsd-core` (#604).
* `get-shit-done` now that the package has been renamed to `msd-core` (#604).
*
* The forbidden token is constructed by splitting across the concat operator
* so this guard script cannot flag itself:
@@ -21,7 +21,7 @@
* release — like CHANGELOG, not swept by rename PRs)
* - Translated READMEs: README.ja-JP.md, README.ko-KR.md, README.pt-BR.md, README.zh-CN.md
* - Locale-specific docs dirs: docs/ja-JP/, docs/ko-KR/, docs/pt-BR/, docs/zh-CN/
* - Lines containing the marker `gsd-allow-legacy-name` (intentional uses)
* - Lines containing the marker `msd-allow-legacy-name` (intentional uses)
* - Binary files (detected by NUL byte scan)
* - This guard script itself (by path)
*
@@ -39,12 +39,12 @@ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const FORBIDDEN = 'get-shit' + '-done';
const FORBIDDEN_RE = new RegExp(FORBIDDEN + '(?!-\\w)', 'gi');
const ALLOW_MARKER = 'gsd-allow-legacy-name';
const ALLOW_MARKER = 'msd-allow-legacy-name';
const SELF_PATH = path.resolve(__filename);
// GSD_LINT_LEGACY_REPO_ROOT is used by tests to redirect the guard to a
// MSD_LINT_LEGACY_REPO_ROOT is used by tests to redirect the guard to a
// temporary fixture git repo without touching the real working tree.
const REPO_ROOT = process.env.GSD_LINT_LEGACY_REPO_ROOT
? path.resolve(process.env.GSD_LINT_LEGACY_REPO_ROOT)
const REPO_ROOT = process.env.MSD_LINT_LEGACY_REPO_ROOT
? path.resolve(process.env.MSD_LINT_LEGACY_REPO_ROOT)
: path.resolve(__dirname, '..');
const ALLOWLIST_FILES = new Set([
@@ -152,7 +152,7 @@ function main() {
process.stderr.write(' ' + v.file + ':' + v.line + ':' + v.col + ' — ' + JSON.stringify(v.text) + '\n');
}
process.stderr.write('\n');
process.stderr.write('Fix: rename to gsd-core, or add `gsd-allow-legacy-name` marker on the line if the\n');
process.stderr.write('Fix: rename to msd-core, or add `msd-allow-legacy-name` marker on the line if the\n');
process.stderr.write(' use is intentional (migration modules, tests, guard, changeset).\n\n');
return 1;
}

View File

@@ -58,7 +58,7 @@ const SEAM_FILE_RE = /(?:^|\/)src\/pattern\.cts$/;
// NOTE: bin/install.js was considered for a blanket "generated bundle,
// downstream mirror" exemption (the same reasoning that excuses
// gsd-core/bin/lib/**/*.cjs below) but REJECTED after inspection —
// msd-core/bin/lib/**/*.cjs below) but REJECTED after inspection —
// bin/install.js:1974 carries a genuine, hand-written `function
// escapeRegExp(value) { return value.replace(...); }` of its own (real
// executable code, not an embedded string copy of another file's content).
@@ -99,8 +99,8 @@ function isSuppressedByComment(line) {
* @param {string} root
*/
function isGeneratedLibMirror(rel, root) {
if (!rel.startsWith('gsd-core/bin/lib/') || !rel.endsWith('.cjs')) return false;
const candidateSrc = `src/${rel.slice('gsd-core/bin/lib/'.length, -'.cjs'.length)}.cts`;
if (!rel.startsWith('msd-core/bin/lib/') || !rel.endsWith('.cjs')) return false;
const candidateSrc = `src/${rel.slice('msd-core/bin/lib/'.length, -'.cjs'.length)}.cts`;
return fs.existsSync(path.join(root, candidateSrc));
}

View File

@@ -4,34 +4,34 @@
/**
* Drift-guard lint for the Package Identity seam (issue #498).
*
* The seam (`get-shit-done/bin/lib/package-identity.cjs`, derived from // gsd-allow-legacy-name
* package.json) is the single source of GSD's published coordinates. Many
* The seam (`get-shit-done/bin/lib/package-identity.cjs`, derived from // msd-allow-legacy-name
* package.json) is the single source of MSD's published coordinates. Many
* runtime surfaces still carry a literal copy of those coordinates because
* they cannot `require()` the seam at runtime: the bash launcher snippet (and
* its byte-equal copies across ~85 workflows, kept in lockstep by the
* runtime-launcher parity test) and the installer's user-facing install/help
* strings.
*
* This lint makes those literals *value-checked*: every GSD package/repo
* This lint makes those literals *value-checked*: every MSD package/repo
* coordinate that appears as a literal must equal the seam's current value.
* It passes today (the literals are correct) and FAILS the moment a repoint is
* not propagated — rename package.json, regenerate the seam, and every stale
* literal is reported until updated. That is what turns a repoint into a
* one-line change with mechanical enforcement.
*
* Scope: the runtime/code surface (bin/, hooks/, scripts/, get-shit-done/). // gsd-allow-legacy-name
* Scope: the runtime/code surface (bin/, hooks/, scripts/, get-shit-done/). // msd-allow-legacy-name
* Pure-prose docs and localized READMEs are intentionally out of scope.
*/
const fs = require('node:fs');
const path = require('node:path');
// A GSD package coordinate: a scoped npm name whose package part contains
// "get-shit-done" (so @opengsd/gsd-sdk and unrelated scopes never match). // gsd-allow-legacy-name
const PACKAGE_RE = /@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]*get-shit-done[A-Za-z0-9._-]*/g; // gsd-allow-legacy-name
// A GSD repo slug, only inside a GitHub URL context so it never overlaps the
// A MSD package coordinate: a scoped npm name whose package part contains
// "get-shit-done" (so @opengsd/gsd-sdk and unrelated scopes never match). // msd-allow-legacy-name
const PACKAGE_RE = /@[A-Za-z0-9._-]+\/[A-Za-z0-9._-]*get-shit-done[A-Za-z0-9._-]*/g; // msd-allow-legacy-name
// A MSD repo slug, only inside a GitHub URL context so it never overlaps the
// scoped package literal above. The `.git` suffix is trimmed before compare.
const SLUG_RE = /(?:github\.com[/:]|raw\.githubusercontent\.com\/)([A-Za-z0-9._-]+\/[A-Za-z0-9._-]*get-shit-done[A-Za-z0-9._-]*)/g; // gsd-allow-legacy-name
const SLUG_RE = /(?:github\.com[/:]|raw\.githubusercontent\.com\/)([A-Za-z0-9._-]+\/[A-Za-z0-9._-]*get-shit-done[A-Za-z0-9._-]*)/g; // msd-allow-legacy-name
function lineOf(text, index) {
let line = 1;
@@ -42,7 +42,7 @@ function lineOf(text, index) {
}
/**
* Pure: find every GSD coordinate literal in `text` that does not match the
* Pure: find every MSD coordinate literal in `text` that does not match the
* expected seam values. Returns [{ kind, found, expected, line }].
*/
function findCoordinateDrift(text, { packageName, repoSlug }) {
@@ -62,14 +62,14 @@ function findCoordinateDrift(text, { packageName, repoSlug }) {
}
// Directories scanned, relative to repo root.
const SCAN_DIRS = ['bin', 'hooks', 'scripts', 'gsd-core'];
const SCAN_DIRS = ['bin', 'hooks', 'scripts', 'msd-core'];
const SCAN_EXT = new Set(['.js', '.cjs', '.sh', '.md']);
// Files exempt because they ARE the source of truth / the tooling that defines
// the coordinate patterns. The generated seam holds the correct value by
// construction; the generator and this lint carry regex/templates, not stray
// literals.
const EXEMPT = new Set([
path.join('gsd-core', 'bin', 'lib', 'package-identity.cjs'),
path.join('msd-core', 'bin', 'lib', 'package-identity.cjs'),
path.join('scripts', 'generate-package-identity.cjs'),
path.join('scripts', 'lint-package-identity-drift.cjs'),
]);
@@ -98,7 +98,7 @@ function walk(dir, acc) {
* annotated with the repo-relative file path.
*/
function scanRepo(root) {
const seam = require(path.join(root, 'gsd-core', 'bin', 'lib', 'package-identity.cjs'));
const seam = require(path.join(root, 'msd-core', 'bin', 'lib', 'package-identity.cjs'));
const expected = { packageName: seam.packageName, repoSlug: seam.repoSlug };
const violations = [];
for (const dir of SCAN_DIRS) {
@@ -124,10 +124,10 @@ function main() {
const root = path.join(__dirname, '..');
const violations = scanRepo(root);
if (violations.length === 0) {
process.stdout.write('ok identity-drift: all GSD coordinate literals match the seam\n');
process.stdout.write('ok identity-drift: all MSD coordinate literals match the seam\n');
return;
}
process.stderr.write('identity-drift: stale GSD coordinate literal(s) found.\n');
process.stderr.write('identity-drift: stale MSD coordinate literal(s) found.\n');
process.stderr.write('Repoint by editing package.json, then `node scripts/generate-package-identity.cjs`,\n');
process.stderr.write('and update the value-checked materialization sites below:\n');
for (const d of violations) {

View File

@@ -202,8 +202,8 @@
* all; only shaped like one because `phasesDir` is a substring of the
* joined path.
* - `src/audit.cts` `listAuditPhaseTargets` (#3458): the shared active-root
* enumeration for the pre-milestone-close audit gate (`gsd-tools.cjs
* audit-open`, called by `/gsd:complete-milestone`'s pre-close gate).
* enumeration for the pre-milestone-close audit gate (`msd-tools.cjs
* audit-open`, called by `/msd:complete-milestone`'s pre-close gate).
* `scanUatGaps`, `scanVerificationGaps`, `scanContextQuestions`, and
* `scanDeferredItems` used to each hand-roll this same readdirSync
* independently (four copies of one re-derivation — the very drift class

View File

@@ -32,7 +32,7 @@
* `hasNameableContent(`.
*
* - shell phase-number-arithmetic ban: `$((10#...))` base-10-forced arithmetic
* inside `gsd-core/workflows/**\/*.md` and `gsd-core/references/**\/*.md` breaks
* inside `msd-core/workflows/**\/*.md` and `msd-core/references/**\/*.md` breaks
* on decimal or multi-segment phase ids and is banned outright. This scan runs
* over markdown, not `.cts` source, so its sanction is an HTML comment on the
* nearest preceding non-blank line: `<!-- phase-id-owner: <reason> -->`.
@@ -40,7 +40,7 @@
* - branch-slug fallback drift: a `.replace('{slug}', ... || 'phase')` call
* silently substitutes the literal string `'phase'` when a phase's slug
* can't be derived, producing a non-identifying branch name like
* `gsd/phase-08-phase` (#4126, now fixed via the shared renderPhaseBranchName
* `msd/phase-08-phase` (#4126, now fixed via the shared renderPhaseBranchName
* owner in phase-id.cts, consumed by both prior call sites). Sanctioned
* the same way as the token/bracket/name-validity rules (`// phase-id-owner:`
* on the nearest preceding non-blank line), with a line-level escape for a
@@ -198,7 +198,7 @@ function findNameValidityDrift(text) {
// #4634: the branch-slug fallback anti-pattern (#4126) — a
// `.replace('{slug}', ... || 'phase')` call silently falls back to the
// literal string `'phase'` when a phase's slug can't be derived, producing a
// non-identifying branch name like `gsd/phase-08-phase`. Now fixed at both
// non-identifying branch name like `msd/phase-08-phase`. Now fixed at both
// prior call sites (commands.cts, init.cts) via the shared
// `renderPhaseBranchName` owner in phase-id.cts; this rule is the ratchet
// against a THIRD site reintroducing the inline fallback. Deliberately
@@ -295,16 +295,16 @@ function findShellPhaseArithDrift(text) {
}
// #4634: the markdown scan roots — shell embedded in workflow/reference docs.
const MD_SCAN_DIRS = [path.join('gsd-core', 'workflows'), path.join('gsd-core', 'references')];
const MD_SCAN_DIRS = [path.join('msd-core', 'workflows'), path.join('msd-core', 'references')];
// #4568 (epic #4634): the single-segment phase regex ban scans a THIRD root,
// `agents/**/*.md`, that the #4619 shell-arithmetic extension above never
// touched — the gsd-code-fixer agent prompts re-derive the phase-number
// touched — the msd-code-fixer agent prompts re-derive the phase-number
// grammar too. Reuses the same `walkMd` walker as the shell-arith scan.
const SINGLE_SEGMENT_SCAN_DIRS = [...MD_SCAN_DIRS, 'agents'];
/**
* Scan `gsd-core/workflows/**\/*.md` and `gsd-core/references/**\/*.md` for
* Scan `msd-core/workflows/**\/*.md` and `msd-core/references/**\/*.md` for
* unsanctioned `$((10#...))` shell arithmetic. Returns [{ file, line, found }]
* with repo-relative paths.
*/
@@ -369,7 +369,7 @@ function findSingleSegmentPhaseRegexDrift(text) {
}
/**
* Scan `gsd-core/workflows/**\/*.md`, `gsd-core/references/**\/*.md`, and
* Scan `msd-core/workflows/**\/*.md`, `msd-core/references/**\/*.md`, and
* `agents/**\/*.md` for unsanctioned single-optional-dotted-segment phase
* regexes. Returns [{ file, line, found }] with repo-relative paths.
*/
@@ -561,7 +561,7 @@ function findShellPhasePrintfPadDrift(text) {
}
/**
* Scan the shell roots (`gsd-core/workflows/**\/*.md`, `gsd-core/references/**\/*.md`)
* Scan the shell roots (`msd-core/workflows/**\/*.md`, `msd-core/references/**\/*.md`)
* for the two shell-idiom rules (a, c) and the three regex roots (those plus
* `agents/**\/*.md`) for the extraction-shape rule (b). Returns
* [{ file, kind, line, found }] with repo-relative paths.
@@ -764,11 +764,11 @@ function main() {
process.stderr.write('the name-validity predicate — or sanction the site with a dedicated\n');
process.stderr.write('`// phase-id-owner: <reason>` comment on the line directly above the regex.\n');
process.stderr.write('`$((10#...))` base-10-forced shell arithmetic is banned outright in\n');
process.stderr.write('gsd-core/workflows/**/*.md and gsd-core/references/**/*.md — sanction with\n');
process.stderr.write('msd-core/workflows/**/*.md and msd-core/references/**/*.md — sanction with\n');
process.stderr.write('`<!-- phase-id-owner: <reason> -->` on the line directly above.\n');
process.stderr.write('The single-optional-dotted-segment phase regex `[0-9]+(\\.[0-9]+)?` (or its \\d\n');
process.stderr.write('near-variant) is banned outright in gsd-core/workflows/**/*.md,\n');
process.stderr.write('gsd-core/references/**/*.md, and agents/**/*.md — widen it to `*` (unbounded\n');
process.stderr.write('near-variant) is banned outright in msd-core/workflows/**/*.md,\n');
process.stderr.write('msd-core/references/**/*.md, and agents/**/*.md — widen it to `*` (unbounded\n');
process.stderr.write('segments) or sanction with `<!-- phase-id-owner: <reason> -->`.\n');
process.stderr.write('A digit-only unbounded-segment phase regex `[0-9]+(\\.[0-9]+)*` on the same roots\n');
process.stderr.write('is missing the canonical letter axis (#4660) — widen to `[0-9]+[A-Z]?(\\.[0-9]+)*`\n');

View File

@@ -6,7 +6,7 @@
* (epic #3180, ADR-3180 §8.4 deliverable C, Phase 12 #3310).
*
* `src/artifacts.cts`'s `isCanonicalPlanningFile` enumerates every file name
* gsd workflows officially write at the `.planning/` ROOT (used today by
* msd workflows officially write at the `.planning/` ROOT (used today by
* `validate.health`'s W019 to flag unrecognized files). Nothing previously
* checked the OTHER direction: that every actual writer of a `.planning/`
* root file is itself represented in that registry. This guard closes that
@@ -49,7 +49,7 @@
* Anything else — a template-literal or otherwise runtime-computed target,
* a multi-segment join landing under `phases/`, `milestones/`, or
* `workstreams/`, a path built through an intermediate helper this guard
* does not recognize (e.g. `path.dirname(x)`, a ternary, a `.gsd/`
* does not recognize (e.g. `path.dirname(x)`, a ternary, a `.msd/`
* fallback) — is silently skipped. This guard reports VIOLATIONS only; a
* skipped write is never counted as a pass either. See the module docblock
* above `src/artifacts.cts` for the registry's own stated scope
@@ -85,7 +85,7 @@ const driftScan = require('./lib/drift-scan.cjs');
const { sanitizeForReport, scanTree } = driftScan;
const REPO_ROOT = path.join(__dirname, '..');
const COMPILED_MODULE_REL = path.join('gsd-core', 'bin', 'lib', 'artifacts.cjs');
const COMPILED_MODULE_REL = path.join('msd-core', 'bin', 'lib', 'artifacts.cjs');
const COMPILED_MODULE_PATH = path.join(REPO_ROOT, COMPILED_MODULE_REL);
// Authored TypeScript source only — mirrors every sibling drift guard.

View File

@@ -10,7 +10,7 @@
* re-derived a second time, in the PROMPT layer: the workflow markdown that
* ships to every runtime, authored as raw shell rather than TypeScript. Issue
* #1762's second reproduction traced a wrong `30 plans, 24 summaries` figure to
* a `ls -1 ... *-PLAN.md | wc -l` snippet in `gsd-core/workflows/progress.md` —
* a `ls -1 ... *-PLAN.md | wc -l` snippet in `msd-core/workflows/progress.md` —
* a re-derivation no `.cts`-scoped guard can see, because it is markdown, not
* source. ADR-3180 Decision 4(a) requires whole-repo discovery; this guard
* extends that requirement from "the whole `src/` tree" to "every authored
@@ -23,7 +23,7 @@
* `[-A-Za-z0-9_.{}$]` characters and then the literal `PLAN.md` or
* `SUMMARY.md`. The leading `*` is load-bearing: it is what makes the
* line enumerate a SET of files rather than name one specific plan.
* `gsd-core/workflows/execute-plan.md`'s
* `msd-core/workflows/execute-plan.md`'s
* `grep -cE '^\s*<task[[:space:]>]' .../{phase}-{plan}-PLAN.md` counts
* TASKS *inside* one already-named plan file — it has no glob token
* (no `*` anywhere near `PLAN.md`), so it is not a plan-count
@@ -33,11 +33,11 @@
* globbing, or merely LISTING plan/summary files (`ls *-PLAN.md`,
* `cat *-PLAN.md`, `--files ".../*-PLAN.md"`) without counting them is
* not this derivation and must not be flagged — every non-counting
* `*-PLAN.md`/`*-SUMMARY.md` glob in `gsd-core/workflows/plan-phase.md`
* `*-PLAN.md`/`*-SUMMARY.md` glob in `msd-core/workflows/plan-phase.md`
* (backup, `--files`, `cat`, cross-reference prose) is exactly this
* shape and is deliberately left alone.
* `*-UAT.md` never matches (a) — UAT artifacts are a different derivation
* this guard does not own — so `gsd-core/workflows/progress.md`'s
* this guard does not own — so `msd-core/workflows/progress.md`'s
* `... *-UAT.md ... | wc -l` line correctly never fires even though it sits
* one line below two lines that DO.
*
@@ -46,7 +46,7 @@
* lint:ci` runs CodeQL js/redos over this repo, the same discipline the
* sibling guards document in their own headers.
*
* Surfaces scanned (SCAN_DIRS): `gsd-core/workflows`, `commands`, `agents`,
* Surfaces scanned (SCAN_DIRS): `msd-core/workflows`, `commands`, `agents`,
* `skills` — the prompt-layer markdown that ships to runtimes. SCAN_EXT:
* `.md` only. The tree-walk / root-confinement / symlink / sanitizer
* machinery is SHARED with the two sibling guards via `scripts/lib/drift-scan.cjs`
@@ -62,7 +62,7 @@
* `scripts/qa-smell-ratchet.cjs`'s precedent exactly: a violation whose
* `(file, text)` pair is already RECORDED in the baseline is KNOWN and never
* fails; a violation whose pair is NOT recorded is NEW and fails, telling the
* author to route the count through the `gsd-core` CLI instead of re-deriving
* author to route the count through the `msd-core` CLI instead of re-deriving
* it in shell; a recorded pair that no longer fires in this run is STALE and
* ALSO fails, forcing `--update` (run by a maintainer after a migration) to
* prune it — this is what makes the baseline SHRINK-ONLY as call sites
@@ -74,7 +74,7 @@
* with this derivation at all.
*
* COUNT, not duplicate rows. Two DIFFERENT source lines can carry the exact
* same (file, TRIMMED text) pair — `gsd-core/workflows/plan-phase.md` has two
* same (file, TRIMMED text) pair — `msd-core/workflows/plan-phase.md` has two
* byte-identical `DISK_PLANS=$(ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null | wc -l
* | tr -d ' ')` sites. Keying on (file, text) alone with one baseline row per
* OCCURRENCE made a partial migration invisible: migrating ONE of the two
@@ -120,9 +120,9 @@ const PLAN_SUMMARY_GLOB_RE = /\*[-A-Za-z0-9_.{}$]*(?:PLAN|SUMMARY)\.md/;
// `scanTree` (scripts/lib/drift-scan.cjs) builds its repo-relative path via
// `path.relative()`, which uses NATIVE separators: on Windows that is
// `gsd-core\workflows\execute-plan.md`, while the committed baseline
// `msd-core\workflows\execute-plan.md`, while the committed baseline
// (`scripts/baselines/planning-prompt-drift-baseline.json`) stores POSIX
// paths (`gsd-core/workflows/execute-plan.md`). Every baseline lookup in this
// paths (`msd-core/workflows/execute-plan.md`). Every baseline lookup in this
// guard is keyed on that path, so an un-normalized Windows path silently
// fails to match ANY baseline entry — every real violation reports as FRESH
// and every baseline entry reports as STALE (100% failure rate on Windows,
@@ -149,7 +149,7 @@ function toPosixRel(relPath) {
const COUNTING_OP_RE = /wc -l|grep -c[A-Za-z]{0,4}\b/;
// Prompt-layer markdown that ships to every runtime.
const SCAN_DIRS = ['gsd-core/workflows', 'commands', 'agents', 'skills'];
const SCAN_DIRS = ['msd-core/workflows', 'commands', 'agents', 'skills'];
const SCAN_EXT = new Set(['.md']);
const BASELINE_REL_PATH = path.join('scripts', 'baselines', 'planning-prompt-drift-baseline.json');
@@ -359,7 +359,7 @@ function writeBaseline(root, violations) {
const doc = {
$comment:
'ADR-3180 Decision 4(e) ratchet, owned by Phase 8 (#3218). See scripts/lint-planning-prompt-drift.cjs. '
+ 'SHRINK-ONLY: entries are removed as sites migrate to the gsd-core CLI; new or changed entries fail '
+ 'SHRINK-ONLY: entries are removed as sites migrate to the msd-core CLI; new or changed entries fail '
+ 'lint:ci. `count` is the number of byte-identical (file, text) occurrences acknowledged at this site '
+ '— a run producing fewer fails as a partial migration, more fails as an unacknowledged new copy.',
entries,
@@ -374,7 +374,7 @@ function writeBaseline(root, violations) {
* Resolve the scan root for a CLI run. Default: this repo — exactly what
* `lint:ci` invokes. `--root <dir>` overrides it (#3640) so the CLI
* end-to-end test can prove the guard's FAIL path against an isolated temp
* tree instead of writing its fixture into the shared `gsd-core/workflows/`
* tree instead of writing its fixture into the shared `msd-core/workflows/`
* that parallel test chunks observe mid-lifecycle (the emitted-provenance
* manifest build copies the transient file into all 19 runtime manifests;
* the #3333 TOCTOU ENOENT crash was the same writer's first symptom).
@@ -435,7 +435,7 @@ function main() {
if (fresh.length > 0) {
process.stderr.write('planning-prompt-drift: NEW plan/summary count re-derivation(s) found in the prompt layer.\n');
process.stderr.write('Route the count through the gsd-core CLI instead of re-deriving it in shell (ls .../*-PLAN.md | wc -l\n');
process.stderr.write('Route the count through the msd-core CLI instead of re-deriving it in shell (ls .../*-PLAN.md | wc -l\n');
process.stderr.write('or grep -c on a *-PLAN.md/*-SUMMARY.md glob), or add an acknowledged entry to\n');
process.stderr.write(`${BASELINE_REL_PATH} via --update:\n`);
for (const v of fresh) {

View File

@@ -2,7 +2,7 @@
'use strict';
/**
* lint-portable-grep.cjs — ban GNU-only `grep -P`/`--perl-regexp` in gsd
* lint-portable-grep.cjs — ban GNU-only `grep -P`/`--perl-regexp` in msd
* workflow / agent / reference / command markdown (#4112 macOS regression).
*
* ## Why
@@ -13,7 +13,7 @@
* `... | grep -oP '...' || true` swallows that error and silently resolves to
* an empty string instead of failing loudly.
*
* This is exactly what happened in `gsd-core/workflows/pause-work.md`'s
* This is exactly what happened in `msd-core/workflows/pause-work.md`'s
* Context Detection step (#4112, merged as #4140): the fix for the `$((`
* shell-syntax bug left `grep -oP 'phases/\K[^/]+'` in place. That construct
* had never been exercised on macOS before (the syntax error masked it), so
@@ -22,7 +22,7 @@
* which the PR's own new regression test then caught, but only in the
* post-merge `full test (macos-latest, ...)` matrix. That lane runs on push
* to the base branch, not on the pull_request event, so nothing pre-merge
* (gsd-test's Linux-only benches included) could have caught it before `next`
* (msd-test's Linux-only benches included) could have caught it before `next`
* went red.
*
* This is the same class of defect `lint-portable-timeout.cjs` (#2351) exists
@@ -70,7 +70,7 @@ const ROOT = path.join(__dirname, '..');
// Surfaces whose markdown carries agent-executed bash. Mirrors
// lint-portable-timeout.cjs's roots — the same files that can ship shell
// snippets which a bash/zsh host on macOS actually executes.
const DEFAULT_ROOTS = ['gsd-core/workflows', 'gsd-core/references', 'agents', 'commands'];
const DEFAULT_ROOTS = ['msd-core/workflows', 'msd-core/references', 'agents', 'commands'];
// A `grep`/`egrep`/`fgrep` token, anchored to a command position (line start,
// right after `| & ; ( \` {`, or right after a shell keyword that opens a new
@@ -153,7 +153,7 @@ function scan(roots = DEFAULT_ROOTS) {
}
function main() {
const rootsEnv = process.env.GSD_LINT_PORTABLE_GREP_ROOTS;
const rootsEnv = process.env.MSD_LINT_PORTABLE_GREP_ROOTS;
const roots = rootsEnv ? rootsEnv.split(path.delimiter).filter(Boolean) : DEFAULT_ROOTS;
const offenders = scan(roots);
if (offenders.length > 0) {

View File

@@ -2,7 +2,7 @@
'use strict';
/**
* lint-portable-timeout.cjs — ban hardcoded GNU-`timeout` in gsd
* lint-portable-timeout.cjs — ban hardcoded GNU-`timeout` in msd
* workflow / agent / reference / command markdown (#2351).
*
* ## Why
@@ -15,7 +15,7 @@
* perfectly good build or test command as a FAILURE (#2351).
*
* The portable, coreutils-independent replacement is the
* `gsd_run run-with-timeout <secs> [--] <cmd…>` verb (gsd-core/bin/gsd-tools.cjs):
* `msd_run run-with-timeout <secs> [--] <cmd…>` verb (msd-core/bin/msd-tools.cjs):
* a Node-based wall-clock cap that keeps GNU `timeout`'s exit-code contract
* (124 on timeout) on every platform. The resolution lives there ONCE and is
* reused by every call site instead of a per-file `command -v timeout` probe.
@@ -25,10 +25,10 @@
*
* ## What PASSES
*
* - `gsd_run run-with-timeout 300 -- bash -c "$CMD"` — the approved verb.
* - `msd_run run-with-timeout 300 -- bash -c "$CMD"` — the approved verb.
* - `command -v timeout` / `command -v gtimeout` / `which timeout` capability
* PROBES — portable: they detect the binary, they do not unconditionally
* execute it (see gsd-core/workflows/review.md's `_AGY_KILLER` fallback).
* execute it (see msd-core/workflows/review.md's `_AGY_KILLER` fallback).
* - Prose ("timed out after 5 minutes"), config keys
* (`workflow.test_gate_timeout`), CI `timeout-minutes:`, the agy
* `--print-timeout` flag, `$TIMEOUT`-style variable names — none of which is
@@ -49,7 +49,7 @@ const ROOT = path.join(__dirname, '..');
// Surfaces whose markdown carries agent-executed bash. Kept broad so the guard
// catches a regression anywhere a workflow snippet could bound a command.
const DEFAULT_ROOTS = ['gsd-core/workflows', 'gsd-core/references', 'agents', 'commands'];
const DEFAULT_ROOTS = ['msd-core/workflows', 'msd-core/references', 'agents', 'commands'];
// Capability probes to strip BEFORE testing for an invocation, so a portable
// `command -v timeout` on the same line is never mistaken for a bare execution.
@@ -119,7 +119,7 @@ function scan(roots = DEFAULT_ROOTS) {
}
function main() {
const rootsEnv = process.env.GSD_LINT_PORTABLE_TIMEOUT_ROOTS;
const rootsEnv = process.env.MSD_LINT_PORTABLE_TIMEOUT_ROOTS;
const roots = rootsEnv ? rootsEnv.split(path.delimiter).filter(Boolean) : DEFAULT_ROOTS;
const offenders = scan(roots);
if (offenders.length > 0) {
@@ -128,7 +128,7 @@ function main() {
1,
'lint-portable-timeout: hardcoded `timeout`/`gtimeout` is not portable — stock\n' +
'macOS ships no coreutils, so these exit 127 and misreport a passing command as a\n' +
'failure. Use `gsd_run run-with-timeout <secs> [--] <cmd…>` instead (#2351):\n' +
'failure. Use `msd_run run-with-timeout <secs> [--] <cmd…>` instead (#2351):\n' +
detail,
);
}

View File

@@ -46,9 +46,9 @@ const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const ROOT = path.join(__dirname, '..');
// Env overrides exist for the lint's own tests only (sandbox fixture dirs).
const TESTS_DIR = process.env.GSD_LINT_REGRESSION_TESTS_DIR || path.join(ROOT, 'tests');
const TESTS_DIR = process.env.MSD_LINT_REGRESSION_TESTS_DIR || path.join(ROOT, 'tests');
const ALLOWLIST_PATH =
process.env.GSD_LINT_REGRESSION_ALLOWLIST ||
process.env.MSD_LINT_REGRESSION_ALLOWLIST ||
path.join(__dirname, 'lint-regression-test-names.allowlist.json');
const BUG_FILE_RE = /^(?:bug|fix|issue)-\d+.*\.test\.cjs$/;

View File

@@ -10,7 +10,7 @@
* consumer (workflows, docs, manifests, npm scripts, tests). #3316: root
* `package-lock.json` was deleted while `package.json` still declares deps
* and workflows still use `cache: 'npm'` + `npm ci` (which require a
* lockfile). e3b52c70: docs referenced a removed `/gsd-new-workspace`
* lockfile). e3b52c70: docs referenced a removed `/msd-new-workspace`
* workflow after it was deleted. #3560: a deleted workflow was pinned by an
* existence assertion in `tests/phase.test.cjs` — the lint passed clean and
* the breakage surfaced only as four red tests on the remote runner,
@@ -21,7 +21,7 @@
* For every file deleted (`git diff --name-status <base>...HEAD`, status
* `D`), grep the post-diff tree for the deleted file's basename:
*
* - `.github/workflows/`, `gsd-core/`, `docs/` (excluding `docs/adr/**` and
* - `.github/workflows/`, `msd-core/`, `docs/` (excluding `docs/adr/**` and
* `docs/research/**` — see "Historical-record exemption" below),
* `package.json` — ANY surviving reference fails (the original rule).
* - `tests/` — scanned with a discriminator (#3565): a reference that PINS
@@ -51,7 +51,7 @@
* ## Basename-collision refinement (#3907)
*
* #3907 deleted `bin/lib/ui-safety-gate.cjs` while the SEPARATE, still-live
* `gsd-core/bin/lib/ui-safety-gate.cjs` survives — every reference to the
* `msd-core/bin/lib/ui-safety-gate.cjs` survives — every reference to the
* survivor (including it referencing itself) was misattributed to the
* deletion, 14 false violations on a correct tree. This epic is about
* de-duplicating modules, so "delete one of two files sharing a basename"
@@ -62,7 +62,7 @@
* post-diff tree — `git ls-files` (tracked files) UNIONED with the
* already filesystem-walked corpus/testsCorpus file lists, because
* `git ls-files` alone misses gitignored BUILD ARTIFACTS such as
* `gsd-core/bin/lib/*.cjs` (compiled from `.cts`, never committed) —
* `msd-core/bin/lib/*.cjs` (compiled from `.cts`, never committed) —
* matching switches from the bare basename to the deleted file's full
* repo-relative path. Because the deletion is already committed on this
* branch, none of these sources can list the deleted file itself, so any
@@ -91,7 +91,7 @@
* The exemption is narrow and applies only to these two directories: every
* other document under `docs/` (guides, `TESTING-SUITES.md`, generated
* indexes, etc.) still enforces "ANY surviving reference fails" exactly as
* before. `.github/workflows/`, `gsd-core/`, and `package.json` are
* before. `.github/workflows/`, `msd-core/`, and `package.json` are
* likewise unaffected — none of those are historical-record surfaces.
*/
@@ -99,11 +99,11 @@ const fs = require('node:fs');
const path = require('node:path');
const cp = require('node:child_process');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { escapeRegex } = require('../gsd-core/bin/lib/pattern.cjs');
const { escapeRegex } = require('../msd-core/bin/lib/pattern.cjs');
const { sanitizeEcho } = require('./command-contract-helpers.cjs');
const ROOT = path.join(__dirname, '..');
const SCAN_ROOTS = ['.github/workflows', 'gsd-core', 'docs'];
const SCAN_ROOTS = ['.github/workflows', 'msd-core', 'docs'];
const EXTRA_FILES = ['package.json'];
const TESTS_ROOT = 'tests';
@@ -133,7 +133,7 @@ function referencesBasename(content, basename) {
* Same delimited-match idea as `referencesBasename`, but the left boundary
* additionally excludes `/` — a `/` immediately to the left would mean the
* matched text is really a SUFFIX of a longer path (e.g. content contains
* `gsd-core/bin/lib/x.cjs` and `relPath` is `bin/lib/x.cjs`: without this,
* `msd-core/bin/lib/x.cjs` and `relPath` is `bin/lib/x.cjs`: without this,
* the survivor's own path would be mistaken for a reference to the
* deleted file it merely shares a basename with).
* @param {string} content
@@ -425,7 +425,7 @@ function scan(root, baseRef) {
// shares the basename, matching switches from basename to full path (see
// the header's "Basename-collision refinement" section) for BOTH arms.
// `git ls-files` alone misses gitignored BUILD ARTIFACTS (e.g.
// gsd-core/bin/lib/*.cjs, compiled from .cts and never committed) —
// msd-core/bin/lib/*.cjs, compiled from .cts and never committed) —
// exactly the #3907 collision partner — so it is unioned with the
// already filesystem-walked corpus/testsCorpus file lists, which do see
// them.
@@ -440,7 +440,7 @@ function scan(root, baseRef) {
}
function main() {
const baseRef = `origin/${process.env.GSD_REMOVED_BUT_NEEDED_BASE || process.env.GITHUB_BASE_REF || 'next'}`;
const baseRef = `origin/${process.env.MSD_REMOVED_BUT_NEEDED_BASE || process.env.GITHUB_BASE_REF || 'next'}`;
let violations;
try {
violations = scan(ROOT, baseRef);

View File

@@ -41,10 +41,10 @@ const fs = require('fs');
const path = require('path');
const ROOT = path.join(__dirname, '..');
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
const WORKFLOWS_DIR = path.join(ROOT, 'msd-core', 'workflows');
// A reference resolves as a sibling of the workflow catalog, so a fixture tree
// and the real one resolve by one rule.
const REFERENCE_ROOT = path.join(ROOT, 'gsd-core');
const REFERENCE_ROOT = path.join(ROOT, 'msd-core');
const DIRECTIVE_REFS = [
'references/response-language-directive.md',
'references/execute-phase-response-language.md',
@@ -91,7 +91,7 @@ const EXACT_INLINE_DIRECTIVE_WORKFLOWS = new Set([
]);
// This lint once carried a third coverage form, for `verify-phase.md`: a
// workflow file nothing entered directly, covered instead by the directive
// execute-phase.md injects into its `gsd-verifier` dispatch prompt. `next`
// execute-phase.md injects into its `msd-verifier` dispatch prompt. `next`
// deleted that workflow in #3421 (an orphan that shipped ~40 KB to every runtime
// and was never loaded) and migrated its live gates into the verifier, so the
// form has no subject left and is gone with it.
@@ -105,7 +105,7 @@ const EXACT_INLINE_DIRECTIVE_WORKFLOWS = new Set([
// positions against in execute-phase.md) directly against the real tree.
// Fragment directories produced by the workflow-fragment epic (#1671). A file
// under one of these is a SECTION of its parent workflow, never an entry point:
// it is reached by a `read and execute gsd-core/workflows/<path>` stub, which
// it is reached by a `read and execute msd-core/workflows/<path>` stub, which
// fires with the parent — and therefore the parent's response-language
// directive — already loaded.
//
@@ -336,7 +336,7 @@ function hasResponseLanguageCoverage(content, refRoot = REFERENCE_ROOT) {
/**
* How far a read/execute verb may sit from the fragment path it governs on the
* same line. Sized from the widest shape the catalog actually emits —
* ``read and execute `gsd-core/workflows/...` `` (14 characters between verb and
* ``read and execute `msd-core/workflows/...` `` (14 characters between verb and
* path) — with room for a variant, and deliberately far short of a sentence, so
* a verb belonging to a different clause cannot reach across and vouch for a
* path it never dispatches.
@@ -359,7 +359,7 @@ const ENTRY_POINT_VERB_RE = new RegExp(`\\b(?:read|execute|run)\\b.{0,${ENTRY_PO
* `<path>` ``, ``Read+execute `<path>` ``, ``Read `<path>` if <condition>``,
* ``run `<path>` to <effect>`` (the spelling #1689's per-plan executor routing
* introduced), and the same stub written with the path RELATIVE to the catalog
* rather than rooted at `gsd-core/workflows/` (#3552's `branching_strategy:
* rather than rooted at `msd-core/workflows/` (#3552's `branching_strategy:
* none` arm). A read/execute/run verb within `ENTRY_POINT_VERB_WINDOW`
* characters ahead of the path on the SAME LINE covers all five.
*
@@ -375,7 +375,7 @@ const ENTRY_POINT_VERB_RE = new RegExp(`\\b(?:read|execute|run)\\b.{0,${ENTRY_PO
* accepted spellings and the rejected mention forms.
*
* SECOND LIMITATION, same class: a dispatch line may sit inside a
* `<!-- gsd:section id="X" when="…" -->` guard, and nothing here checks that the
* `<!-- msd:section id="X" when="…" -->` guard, and nothing here checks that the
* guard is satisfiable or that its `id` still matches the fragment it gates.
* Every such pairing lines up today. If one drifts, the fragment becomes
* unreachable while this function still calls it dispatched — an orphan the lint
@@ -388,7 +388,7 @@ function namesFragmentAsEntryPoint(parent, relative) {
// #3552's `"none"` arm introduced the second one — `Read and execute
// `execute-phase/steps/protected-branch.md`` — and a rooted-only needle read
// that live dispatch as no dispatch at all.
const needles = [`gsd-core/workflows/${relative}`, relative];
const needles = [`msd-core/workflows/${relative}`, relative];
return parent.split(/\r?\n/).some((line) => needles.some((needle) => {
const at = line.indexOf(needle);
// A path character immediately before the match means this is the TAIL of

View File

@@ -2,11 +2,11 @@
/**
* lint-retired-runtime-name.cjs
*
* Prevents a RETIRED GSD runtime name from being presented as if it were a
* Prevents a RETIRED MSD runtime name from being presented as if it were a
* live runtime (#1928 retired the "Gemini CLI" runtime lane in favor of
* Antigravity). Mirrors scripts/lint-legacy-dir-name.cjs's structure and
* conventions (same problem shape: forbid a retired token, allowlist
* frozen/legitimate content, self-exempt, inline marker, `GSD_LINT_*_REPO_ROOT`
* frozen/legitimate content, self-exempt, inline marker, `MSD_LINT_*_REPO_ROOT`
* test seam, ./lib/cli-exit.cjs, binary-file skip).
*
* THE PREDICATE (load-bearing — do not "simplify"):
@@ -66,7 +66,7 @@ const { execFileSync } = require('child_process');
const fs = require('fs');
const path = require('path');
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
const { escapeRegex } = require('../gsd-core/bin/lib/pattern.cjs');
const { escapeRegex } = require('../msd-core/bin/lib/pattern.cjs');
// Table of retired runtimes. Each name is constructed via split-string
// concatenation so this guard script cannot flag itself when scanned.
@@ -89,21 +89,21 @@ const RETIRED_RUNTIME_MATCHERS = RETIRED_RUNTIMES.map((r) => ({
// must never silently report "clean" (anti-vacuity requirement).
const MIN_EXPECTED_FILES = 150;
const ALLOW_MARKER = 'gsd-allow-retired-runtime-name';
const ALLOW_MARKER = 'msd-allow-retired-runtime-name';
// The escape hatch must carry a justification. Bare-marker abuse was raised in
// review: the marker is checked first, excuses the whole line, and the failure
// message advertises it, so an unexplained one is indistinguishable from a
// defect somebody silenced. Require `gsd-allow-retired-runtime-name: <reason>`.
// defect somebody silenced. Require `msd-allow-retired-runtime-name: <reason>`.
const ALLOW_MARKER_RE = new RegExp(escapeRegex(ALLOW_MARKER) + ':\\s*\\S{3,}');
const SELF_PATH = path.resolve(__filename);
// GSD_LINT_RETIRED_RUNTIME_REPO_ROOT is used by tests to redirect the guard
// MSD_LINT_RETIRED_RUNTIME_REPO_ROOT is used by tests to redirect the guard
// to a temporary fixture git repo without touching the real working tree.
const REPO_ROOT = process.env.GSD_LINT_RETIRED_RUNTIME_REPO_ROOT
? path.resolve(process.env.GSD_LINT_RETIRED_RUNTIME_REPO_ROOT)
const REPO_ROOT = process.env.MSD_LINT_RETIRED_RUNTIME_REPO_ROOT
? path.resolve(process.env.MSD_LINT_RETIRED_RUNTIME_REPO_ROOT)
: path.resolve(__dirname, '..');
// Every tracked *.md file is in scope. An earlier prefix list
// (docs/ gsd-core/ commands/ agents/ skills/ + root) left `.changeset/`,
// (docs/ msd-core/ commands/ agents/ skills/ + root) left `.changeset/`,
// `.github/`, `capabilities/`, `playbooks/` and `references/` invisible —
// and `.changeset/*.md` RENDERS INTO CHANGELOG.md, which is blanket-trusted,
// so a live claim introduced there was invisible at both ends.
@@ -215,13 +215,13 @@ const ALLOWLIST_OCCURRENCES = new Map([
['docs/zh-CN/how-to/install-on-your-runtime.md', [
{ snippet: '与 Gemini 兼容的设置策略', reason: 'settings dialect Antigravity inherits' },
]],
['gsd-core/workflows/reapply-patches.md', [
['msd-core/workflows/reapply-patches.md', [
{ snippet: 'pre-#1928 Gemini CLI install', reason: 'legacy-install patch location, explicitly pre-retirement' },
]],
['gsd-core/workflows/settings-advanced.md', [
{ snippet: '(Claude / OpenAI / Gemini / Qwen)', reason: 'model-provider menu, not a GSD runtime' },
['msd-core/workflows/settings-advanced.md', [
{ snippet: '(Claude / OpenAI / Gemini / Qwen)', reason: 'model-provider menu, not a MSD runtime' },
]],
['gsd-core/references/ai-frameworks.md', [
['msd-core/references/ai-frameworks.md', [
{ snippet: 'Google Cloud / Gemini-committed teams', reason: 'provider axis (Google ADK)' },
{ snippet: 'Optimized for Gemini; supports other models', reason: 'model axis' },
{ snippet: 'teams already committed to Gemini', reason: 'model axis' },
@@ -229,13 +229,13 @@ const ALLOWLIST_OCCURRENCES = new Map([
{ snippet: 'Gemini vendor lock-in in practice', reason: 'model axis' },
{ snippet: 'Google/Gemini-committed', reason: 'provider axis (Google ADK)' },
]],
['agents/gsd-framework-selector.md', [
['agents/msd-framework-selector.md', [
// Two occurrences on one line: the menu label and its description.
{ snippet: 'Google (Gemini)', reason: 'model-provider choice, not a GSD runtime' },
{ snippet: 'Google (Gemini)', reason: 'model-provider choice, not a MSD runtime' },
{ snippet: 'Committed to Gemini / Google Cloud / Vertex AI', reason: 'the same menu entry\'s description' },
]],
['agents/gsd-framework-selector.compact.md', [
{ snippet: 'Google (Gemini)', reason: 'model-provider choice, not a GSD runtime' },
['agents/msd-framework-selector.compact.md', [
{ snippet: 'Google (Gemini)', reason: 'model-provider choice, not a MSD runtime' },
]],
]);
@@ -392,7 +392,7 @@ const RUNTIME_WORD_RE = new RegExp(
// Positive evidence that the line is on the MODEL axis. Required, not merely
// the absence of a runtime word: a reviewer demonstrated that "absence of a
// veto word" is not evidence, with `The installer now offers Gemini 3.`,
// `GSD installs cleanly on Gemini 3, Kimi, and Codex.` and
// `MSD installs cleanly on Gemini 3, Kimi, and Codex.` and
// `Supported agents include Gemini 3, Kimi, and Cursor.` all exiting 0 — the
// exact laundering class this guard exists to catch. Every real model-axis
// line in this repo names a model explicitly, so requiring it costs nothing
@@ -417,9 +417,9 @@ const MODEL_WORD_RE = new RegExp(
* of the pin's snippet, not merely share a line with it.
*
* Line-level containment was too weak: a reviewer showed
* `Known provider menu update: Gemini CLI is once again a selectable GSD
* `Known provider menu update: Gemini CLI is once again a selectable MSD
* runtime.` and `Install target: Google (Gemini) — choose Gemini CLI as your
* GSD runtime.` both exiting 0, because a short snippet elsewhere on the line
* MSD runtime.` both exiting 0, because a short snippet elsewhere on the line
* pre-approved a brand-new claim. Span containment means a pin can only ever
* excuse the occurrence its own text covers.
*/

View File

@@ -7,10 +7,10 @@
* registered in eslint.config.mjs and its source file exists, or the named
* test file exists on disk. Deliberately resolves-only (maintainer decision,
* chat, 2026-08-27): it does not verify the mechanism's surface actually
* covers the seam's files — see .gsd/phase/feat-3626-context-seam-claim-gate/40-design.md.
* covers the seam's files — see .msd/phase/feat-3626-context-seam-claim-gate/40-design.md.
*
* Design: .gsd/phase/feat-3626-context-seam-claim-gate/40-design.md
* Test matrix: .gsd/phase/feat-3626-context-seam-claim-gate/50-test-matrix.md
* Design: .msd/phase/feat-3626-context-seam-claim-gate/40-design.md
* Test matrix: .msd/phase/feat-3626-context-seam-claim-gate/50-test-matrix.md
*
* Usage:
* node scripts/lint-seam-enforcement.cjs [path-to-context-md]

View File

@@ -54,7 +54,7 @@ function main() {
for (const match of matches) {
process.stderr.write(` - ${match.label}\n`);
}
process.stderr.write('Route shim/wrapper rendering through gsd-core/bin/lib/shell-command-projection.cjs\n');
process.stderr.write('Route shim/wrapper rendering through msd-core/bin/lib/shell-command-projection.cjs\n');
process.stderr.write('Safe subprocess execution via spawnSync/execFileSync is intentionally allowed.\n');
return 1;
}

View File

@@ -5,8 +5,8 @@
* Two checks:
*
* a) Frontmatter to body consistency:
* For each commands/gsd/*.md, parse requires: and walk the body for
* references to other GSD skills (pattern: /gsd:<stem> or gsd:<stem>).
* For each commands/msd/*.md, parse requires: and walk the body for
* references to other MSD skills (pattern: /msd:<stem> or msd:<stem>).
* Fail if a skill body references a skill not listed in requires:.
*
* b) Profile closure satisfaction:
@@ -15,7 +15,7 @@
* the closure references a skill NOT in the closure, fail.
*
* Usage:
* node scripts/lint-skill-deps.cjs # scans commands/gsd/
* node scripts/lint-skill-deps.cjs # scans commands/msd/
* node scripts/lint-skill-deps.cjs --dir <path> # scan a custom dir (testing)
*
* Exits 0 if all pass; exits 1 if any violation.
@@ -27,14 +27,14 @@ const fs = require('fs');
const path = require('path');
const { runMain } = require('./lib/cli-exit.cjs');
const PROFILES_MODULE = path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'install-profiles.cjs');
const PROFILES_MODULE = path.join(__dirname, '..', 'msd-core', 'bin', 'lib', 'install-profiles.cjs');
const { PROFILES, loadSkillsManifest, resolveProfile } = require(PROFILES_MODULE);
// ---------------------------------------------------------------------------
// Argument parsing
// ---------------------------------------------------------------------------
let commandsDir = path.join(__dirname, '..', 'commands', 'gsd');
let commandsDir = path.join(__dirname, '..', 'commands', 'msd');
const args = process.argv.slice(2);
for (let i = 0; i < args.length; i++) {
if (args[i] === '--dir' && args[i + 1]) {
@@ -49,7 +49,7 @@ for (let i = 0; i < args.length; i++) {
function extractBodyReferences(body) {
const refs = new Set();
const re = /(?:\/?)gsd:([a-z0-9_-]+)/g;
const re = /(?:\/?)msd:([a-z0-9_-]+)/g;
let m;
while ((m = re.exec(body)) !== null) {
refs.add(m[1]);
@@ -89,7 +89,7 @@ function checkFrontmatterBodyConsistency(manifest, allStems) {
stem,
filePath,
undeclared: ref,
message: 'body references unknown skill gsd:' + ref,
message: 'body references unknown skill msd:' + ref,
});
continue;
}
@@ -98,7 +98,7 @@ function checkFrontmatterBodyConsistency(manifest, allStems) {
stem,
filePath,
undeclared: ref,
message: 'body references gsd:' + ref + ' but requires: does not list it',
message: 'body references msd:' + ref + ' but requires: does not list it',
});
}
}

View File

@@ -91,7 +91,7 @@
* guard its precision.
*
* SCOPE. `src/`, `scripts/`, `tests/`, `eslint-rules/` — NOT
* `gsd-core/bin/lib/**` or `bin/install.js`, which are `src/`'s own BUILT
* `msd-core/bin/lib/**` or `bin/install.js`, which are `src/`'s own BUILT
* OUTPUT (via `npm run build:lib` / the installer bundling step): scanning
* them in addition to `src/` would double-count every authored re-derivation
* once for its source and once for its compiled mirror. Both are simply
@@ -149,7 +149,7 @@ const driftScan = require('./lib/drift-scan.cjs');
const { MAX_REGEX_LITERAL_LEN, sanitizeForReport, scanTree, readRegexLiteralAt } = driftScan;
// Authored source across the four surfaces the brief scopes this guard to.
// `gsd-core/bin/lib/**` (src/'s build output) and `bin/install.js` are never
// `msd-core/bin/lib/**` (src/'s build output) and `bin/install.js` are never
// visited because they are not in this list — see the header comment.
const SCAN_DIRS = ['src', 'scripts', 'tests', 'eslint-rules'];
// `.mjs`/`.tsx`/`.jsx` added (MINOR fix): the original set silently never

View File

@@ -11,7 +11,7 @@
* test) whose filename happened to match Node's `test-*` collection
* convention. `scripts/run-tests.cjs` — what local `npm test` and GitHub CI
* use — globs only `tests/**\/*.test.cjs`, so CI never saw it. But the
* REMOTE test runner (the push gate; see CLAUDE.md's `gsd-test` section)
* REMOTE test runner (the push gate; see CLAUDE.md's `msd-test` section)
* collects test files the way `node --test` does by default, across the
* whole tree, so it picked the file up and executed it AS a test, where it
* exited 1. Net effect: `next` was green on GitHub CI and red on the push
@@ -42,20 +42,20 @@
*
* ## Scanned (source/shipped) directories
*
* src/, scripts/, hooks/, bin/, gsd-core/bin/ (excluding
* gsd-core/bin/lib/**, see below), eslint-rules/
* src/, scripts/, hooks/, bin/, msd-core/bin/ (excluding
* msd-core/bin/lib/**, see below), eslint-rules/
*
* ## Exempted
*
* - tests/ — files there are SUPPOSED to match; that is the point.
* - node_modules/, .git/ — never source we own.
* - gsd-core/bin/lib/** — build output generated from src/*.cts by
* - msd-core/bin/lib/** — build output generated from src/*.cts by
* `npm run build:lib` (tsc), and gitignored (verified: every file under
* it, including the incident's own post-fix
* `gsd-core/bin/lib/real-home-guard.cjs`, is listed in .gitignore). A
* `msd-core/bin/lib/real-home-guard.cjs`, is listed in .gitignore). A
* generated file inherits its source's basename 1:1, so scanning it
* would double-report the exact same defect `src/` already caught —
* noise, not signal. `gsd-core/bin/shared/*.json` is data, not code,
* noise, not signal. `msd-core/bin/shared/*.json` is data, not code,
* but is harmlessly included since it never matches a JS/TS extension.
*
* Exported pure(ish) function `checkSourceTestNameCollisions({ dirs, root })`
@@ -92,20 +92,20 @@ const BASENAME_PATTERNS = [
/**
* Source/shipped directories this guard checks, relative to repo root.
* Confirmed against the repo layout: src/, scripts/, hooks/, bin/,
* gsd-core/bin/, eslint-rules/ all ship first-party source or shipped
* msd-core/bin/, eslint-rules/ all ship first-party source or shipped
* tooling; nothing else at the top level carries executable source outside
* tests/.
*/
const DEFAULT_SCAN_DIRS = ['src', 'scripts', 'hooks', 'bin', 'gsd-core/bin', 'eslint-rules'];
const DEFAULT_SCAN_DIRS = ['src', 'scripts', 'hooks', 'bin', 'msd-core/bin', 'eslint-rules'];
// Directories to never descend into anywhere in the tree.
const ALWAYS_EXCLUDE_DIR_NAMES = new Set(['node_modules', '.git']);
// Relative dir prefixes (POSIX-joined, relative to repo root) that are
// generated build output and must not be scanned — see the module doc for
// why gsd-core/bin/lib is excluded (it 1:1-inherits src/*.cts basenames, so
// why msd-core/bin/lib is excluded (it 1:1-inherits src/*.cts basenames, so
// scanning it double-reports the same defect src/ already catches).
const GENERATED_OUTPUT_PREFIXES = ['gsd-core/bin/lib'];
const GENERATED_OUTPUT_PREFIXES = ['msd-core/bin/lib'];
function toPosix(p) {
return p.split(path.sep).join('/');

View File

@@ -20,7 +20,7 @@
* WHOLE `src/` TREE, not by consulting an allowlist of known files. Per
* Decision 4(d) that surface is widened further still: `src/` alone is
* itself the forbidden allowlist, one directory wide, so this guard ALSO
* scans the prompt-layer markdown (`gsd-core/workflows`, `commands`,
* scans the prompt-layer markdown (`msd-core/workflows`, `commands`,
* `agents`, `skills`) for a PROSE re-derivation of the same chain — see the
* "PROMPT-LAYER PROSE DETECTION" section below `findStateFieldDrift`.
*
@@ -300,14 +300,14 @@ const ARROW_CONST_RE = /\bconst\s+([A-Za-z_$][\w$]*)\s*=\s*\([^)]*\)\s*(?::\s*[^
// wide. This derivation is expressed in TWO languages: TypeScript under
// `src/` (the ladder + `stateExtractField(` shape PASS 1/2 above detect), and
// PROSE in the workflow/command/agent/skill markdown that ships to every
// runtime — `gsd-core/workflows/smart-entry.md`'s "Extract: `status`
// runtime — `msd-core/workflows/smart-entry.md`'s "Extract: `status`
// (frontmatter `status:` or body `**Status:**`)" is exactly this chain,
// hand-described rather than called. `SCAN_DIRS` therefore covers both;
// `findPromptFieldDrift` (below `findStateFieldDrift`) is the markdown-side
// detector, dispatched by extension in `scanRepo`'s `onFile`. Mirrors
// `lint-planning-prompt-drift.cjs`'s own prompt-layer surface exactly
// (`gsd-core/workflows`, `commands`, `agents`, `skills`).
const SCAN_DIRS = ['src', 'gsd-core/workflows', 'commands', 'agents', 'skills'];
// (`msd-core/workflows`, `commands`, `agents`, `skills`).
const SCAN_DIRS = ['src', 'msd-core/workflows', 'commands', 'agents', 'skills'];
const SCAN_EXT = new Set(['.cts', '.ts', '.mts', '.md']);
// The designated owner (issue #3187 Phase 5, ADR-3180 §7.7).
@@ -656,8 +656,8 @@ const BODY_BOLD_TOKEN_RE = /`\*\*[^*`]{1,80}\*\*`/;
const FRONTMATTER_WORD_RE = /\bfrontmatter\b/i;
const BODY_WORD_RE = /\bbody\b/i;
// Per ADR-3180 Decision 4(d)/(e): `gsd-core/workflows/smart-entry.md`'s
// Fallback step 1 ("`gsd-tools` itself is broken") is a PERMANENT, by-
// Per ADR-3180 Decision 4(d)/(e): `msd-core/workflows/smart-entry.md`'s
// Fallback step 1 ("`msd-tools` itself is broken") is a PERMANENT, by-
// construction exemption — NOT debt with an owner, and therefore NOT
// modelled as `lint-planning-prompt-drift.cjs`'s shrink-only ratchet
// baseline (which exists specifically to acknowledge sites with a removal
@@ -678,11 +678,11 @@ const BODY_WORD_RE = /\bbody\b/i;
// consistency within the one file).
const PROMPT_LAYER_EXEMPTIONS = new Map([
[
path.join('gsd-core', 'workflows', 'smart-entry.md'),
path.join('msd-core', 'workflows', 'smart-entry.md'),
new Map([
[
"- Read `.planning/STATE.md` (frontmatter + body) with the Read tool. Extract: `status` (frontmatter `status:` or body `**Status:**`), `Phase:` from the body, `total_phases`/`percent` from a nested `progress:` frontmatter object if present, and any `## Blockers` items.",
'gsd-tools-down fallback (smart-entry.md Fallback step 1): runs only when gsd-tools itself cannot run, so it cannot call the canonical owner it substitutes for — permanent by construction, not removable debt.',
'msd-tools-down fallback (smart-entry.md Fallback step 1): runs only when msd-tools itself cannot run, so it cannot call the canonical owner it substitutes for — permanent by construction, not removable debt.',
],
]),
],

View File

@@ -15,7 +15,7 @@
* guard used to track as a RATCHETED STRING MATCH against
* `scripts/state-write-path-drift-baseline.json` — `cmdStateSync`
* (`src/state.cts`, #905's "let the body win") and `REGENERATE_STATE`
* (`src/health-diagnostic.cts`, `/gsd-health --repair`'s factory reset) — are
* (`src/health-diagnostic.cts`, `/msd-health --repair`'s factory reset) — are
* NO LONGER TRACKED HERE. Both are now a constructor the type system names
* (`rebuildStateTransaction`), not an entry a human had to remember to keep
* acknowledging in a baseline file. That baseline file, and the whole
@@ -79,7 +79,7 @@
* dataflow — see the function's own docstring).
*
* AXIS 4 — PROMPT-LAYER WRITE (§8.3, Decision 4(d)). Prose in the prompt
* layer instructing an agent to shell out to a write-side `gsd-tools`
* layer instructing an agent to shell out to a write-side `msd-tools`
* subcommand is the same write seam, expressed as markdown rather than
* TypeScript — a check the type system cannot reach at all, since markdown
* is never compiled. Any occurrence is a violation.
@@ -100,7 +100,7 @@
* nothing.
* - 4(d) the scan surface is DECLARED and is NOT just `src/` — `src/`
* alone is itself an allowlist one directory wide; #1762 traced a wrong
* count to a shell snippet in `gsd-core/workflows/progress.md`.
* count to a shell snippet in `msd-core/workflows/progress.md`.
*
* GOODHART, PER ADR-3408 DECISION 5: "0 violations" is a LAGGING metric — a
* measure about to become a target. This guard's own `_comment` and its
@@ -133,7 +133,7 @@
* #3871 review) all remain, because §8.6 names neither them nor anything
* that makes what they check unrepresentable — a field-name-keyed dispatch
* branch, an unimplemented `FieldPreservation` policy, an unstripped
* frontmatter write, prompt-layer prose shelling out to `gsd-tools`, and a
* frontmatter write, prompt-layer prose shelling out to `msd-tools`, and a
* re-assembled write-seam composition are all still exactly as representable
* in TypeScript (or in markdown, for the prompt-layer one) after the
* state-transaction constructor as they were before it — the constructor
@@ -144,7 +144,7 @@
const path = require('node:path');
const { scanTree, sanitizeForReport } = require('./lib/drift-scan.cjs');
const { escapeRegex } = require('../gsd-core/bin/lib/pattern.cjs');
const { escapeRegex } = require('../msd-core/bin/lib/pattern.cjs');
const REPO_ROOT = path.resolve(__dirname, '..');
@@ -169,7 +169,7 @@ const REASON = Object.freeze({
// wired terminal in C2 as the residual callers migrate (ADR-3408 §6 phasing).
OPAQUE_STATE_TRANSFORM: 'opaque_state_transform',
// Axis 4 (§8.3, Decision 4(d)): prompt-layer prose shelling out to a
// write-side `gsd-tools` subcommand — see `findPromptSeamUses`.
// write-side `msd-tools` subcommand — see `findPromptSeamUses`.
PROMPT_LAYER_STATE_WRITE: 'prompt_layer_state_write',
// Axis 5 (§8.3, RETAINED, issue #3871): a direct `syncStateFrontmatter(` or
// `applyPostSyncPreservation(` call outside their owner
@@ -183,7 +183,7 @@ const REASON = Object.freeze({
// result outside any TypeScript this guard could see.
const SRC_DIRS = ['src'];
const SRC_EXT = new Set(['.cts']);
const PROMPT_DIRS = ['gsd-core/workflows', 'commands', 'agents', 'skills'];
const PROMPT_DIRS = ['msd-core/workflows', 'commands', 'agents', 'skills'];
const PROMPT_EXT = new Set(['.md']);
// The executor (Axis 1 / Axis 3). Forward-slash literal: every `rel` this
@@ -783,7 +783,7 @@ function findCompositionBypasses(rel, text) {
return out;
}
// Prose in the prompt layer shelling out to a write-side `gsd-tools`
// Prose in the prompt layer shelling out to a write-side `msd-tools`
// subcommand — the SAME write seam, expressed as markdown instructing an
// agent to run a command, rather than TypeScript calling a function
// directly (Decision 4(d)'s "the scan surface is declared, and is not just
@@ -791,7 +791,7 @@ function findCompositionBypasses(rel, text) {
// carries no comment syntax this guard should be stripping in the first
// place. `g`-flagged so multiple candidate occurrences on one line are all
// checked against backtick spans below, rather than only the first.
const PROMPT_SEAM_RE = /gsd[-_]?tools[^\n]*\b(state\.patch|state\.planned-phase|state\.sync|phase\.complete)\b/g;
const PROMPT_SEAM_RE = /msd[-_]?tools[^\n]*\b(state\.patch|state\.planned-phase|state\.sync|phase\.complete)\b/g;
/**
* Every `` `...` `` inline-code span on `line`, as `[start, end)` character
@@ -822,7 +822,7 @@ function isInsideCodeSpan(line, index) {
/**
* AXIS 4: every prompt-layer line instructing an agent to shell out to a
* write-side `gsd-tools` subcommand. Terminal (unratcheted): any occurrence
* write-side `msd-tools` subcommand. Terminal (unratcheted): any occurrence
* is a violation — this baseline was always empty for the prompt layer (no
* prompt-layer entry was ever acknowledged), so removing the ratchet changes
* nothing observable here.

View File

@@ -6,8 +6,8 @@
* "prohibition with teeth", epic #2143).
*
* `TABLE_SCHEMAS` (`src/markdown-table.cts`, compiled to
* `gsd-core/bin/lib/markdown-table.cjs`) is the single-source registry naming
* every canonical GSD pipe-table's column-header shape. Each registered
* `msd-core/bin/lib/markdown-table.cjs`) is the single-source registry naming
* every canonical MSD pipe-table's column-header shape. Each registered
* variant's exact `| col | col |` header string MUST appear verbatim in the
* ONE template/workflow file that emits that table — otherwise the writer and
* the registry have silently drifted apart, the exact bug class this ADR
@@ -26,10 +26,10 @@ const path = require('node:path');
// Schema id -> the ONE canonical template/workflow file that must emit every
// variant's header verbatim (ADR-2143 §3).
const SCHEMA_SOURCE_FILES = {
RoadmapProgress: path.join('gsd-core', 'templates', 'roadmap.md'),
RequirementsTraceability: path.join('gsd-core', 'templates', 'requirements.md'),
QuickTasks: path.join('gsd-core', 'workflows', 'quick.md'),
Security: path.join('gsd-core', 'templates', 'SECURITY.md'),
RoadmapProgress: path.join('msd-core', 'templates', 'roadmap.md'),
RequirementsTraceability: path.join('msd-core', 'templates', 'requirements.md'),
QuickTasks: path.join('msd-core', 'workflows', 'quick.md'),
Security: path.join('msd-core', 'templates', 'SECURITY.md'),
};
/** Build the exact `| a | b | c |` header line for one schema variant. */
@@ -102,7 +102,7 @@ function findTableSchemaDrift(schemas, readFile, sourceFiles = SCHEMA_SOURCE_FIL
* build:lib`), reports a single actionable violation rather than throwing.
*/
function scanRepo(root) {
const seamPath = path.join(root, 'gsd-core', 'bin', 'lib', 'markdown-table.cjs');
const seamPath = path.join(root, 'msd-core', 'bin', 'lib', 'markdown-table.cjs');
let seam;
try {
seam = require(seamPath);

View File

@@ -8,7 +8,7 @@
"ui-consideration-probe.test.cjs"
],
"issue": "4657",
"justification": "The ui-consideration-probe cluster gains its workflow-prose contract file (tests/ui-consideration-probe-ui-phase-contract.test.cjs, #4657), mirroring the edge probe's split (edge-probe.test.cjs / edge-probe-docs-fixtures.test.cjs / edge-probe-spec-phase-contract.test.cjs). The contract file reads shipped gsd-core/workflows/ui-phase.md under the source-text-is-the-product exception, which the unit file deliberately does not carry (it asserts typed returns only)."
"justification": "The ui-consideration-probe cluster gains its workflow-prose contract file (tests/ui-consideration-probe-ui-phase-contract.test.cjs, #4657), mirroring the edge probe's split (edge-probe.test.cjs / edge-probe-docs-fixtures.test.cjs / edge-probe-spec-phase-contract.test.cjs). The contract file reads shipped msd-core/workflows/ui-phase.md under the source-text-is-the-product exception, which the unit file deliberately does not carry (it asserts typed returns only)."
},
"adr-parser": {
"files": [
@@ -292,7 +292,7 @@
"ui-consideration-probe.test.cjs"
],
"issue": "4657",
"justification": "The ui-consideration-probe cluster gains its workflow-prose contract file (tests/ui-consideration-probe-ui-phase-contract.test.cjs, #4657), mirroring the edge probe's split (edge-probe.test.cjs / edge-probe-docs-fixtures.test.cjs / edge-probe-spec-phase-contract.test.cjs). The contract file reads shipped gsd-core/workflows/ui-phase.md under the source-text-is-the-product exception, which the unit file deliberately does not carry (it asserts typed returns only)."
"justification": "The ui-consideration-probe cluster gains its workflow-prose contract file (tests/ui-consideration-probe-ui-phase-contract.test.cjs, #4657), mirroring the edge probe's split (edge-probe.test.cjs / edge-probe-docs-fixtures.test.cjs / edge-probe-spec-phase-contract.test.cjs). The contract file reads shipped msd-core/workflows/ui-phase.md under the source-text-is-the-product exception, which the unit file deliberately does not carry (it asserts typed returns only)."
}
}
}

View File

@@ -2,7 +2,7 @@
/**
* lint-test-file-count.cjs — max 2 test files per production module.
*
* Scans sdk/src/query/, sdk/src/, gsd-core/bin/lib/, bin/ for production
* Scans sdk/src/query/, sdk/src/, msd-core/bin/lib/, bin/ for production
* modules, then counts matching test files in tests/ and sdk/src (recursive). Cap is 2
* (primary + one integration). Over-limit clusters must be in the allowlist with the
* EXACT set of test filenames grandfathered (identity ratchet via allowlist-ratchet.cjs).
@@ -23,7 +23,7 @@ const ROOT = path.join(__dirname, '..');
const PROD_DIRS = [
path.join(ROOT, 'sdk', 'src', 'query'),
path.join(ROOT, 'sdk', 'src'),
path.join(ROOT, 'gsd-core', 'bin', 'lib'),
path.join(ROOT, 'msd-core', 'bin', 'lib'),
path.join(ROOT, 'bin'),
];
const TEST_DIRS = [

View File

@@ -5,19 +5,19 @@
* Prompt-layer drift guard for #3409 — shell guards that cannot observe
* their own failure arm.
*
* Design: .gsd/phase/feat-3409-unreachable-shell-guard-lint/40-design.md
* Test matrix: .gsd/phase/feat-3409-unreachable-shell-guard-lint/50-test-matrix.md
* Design: .msd/phase/feat-3409-unreachable-shell-guard-lint/40-design.md
* Test matrix: .msd/phase/feat-3409-unreachable-shell-guard-lint/50-test-matrix.md
*
* RETIRED — Detector A (`--pick` + `|| echo` on one line), #3884.
* `gsd-tools.cjs`'s `--pick <field>` extractor used to coerce a missing/
* `msd-tools.cjs`'s `--pick <field>` extractor used to coerce a missing/
* absent field to the empty string and exit **0**, which made the `|| echo D`
* arm in `$(gsd_run query V --pick F 2>/dev/null || echo D)` unreachable on
* arm in `$(msd_run query V --pick F 2>/dev/null || echo D)` unreachable on
* field absence — the exact defect Detector A existed to flag (this file's
* own prior header quoted the premise verbatim: "the `|| echo D` arm can
* fire only on a typo in the verb name, never on the field absence it was
* written to handle"). ADR-3473 §8.4 ("Failure is a value") makes `--pick`
* exit **non-zero** on an absent field (see
* `.gsd/phase/feat-3884-failure-is-a-value/40-design.md` rows B6-B14), so
* `.msd/phase/feat-3884-failure-is-a-value/40-design.md` rows B6-B14), so
* that premise is now FALSE: the `|| echo D` arm is reachable, and the shape
* Detector A forbade is the CORRECT idiom going forward. Keeping Detector A
* would forbid the fix, so it is removed rather than updated — see this
@@ -118,7 +118,7 @@
* over fixed literals, then a single trailing `(.*)$` — again one
* quantifier, no nesting.
*
* ESCAPE MARKER. A line carrying `# gsd-scan-ignore: <reason>` is exempt
* ESCAPE MARKER. A line carrying `# msd-scan-ignore: <reason>` is exempt
* ONLY when `<reason>` names an issue (`#NNN`, N a positive integer) or an
* `http(s)://` URL with an actual host after the scheme — the repo's
* existing precedent from `tests/commit-files-pathspec.test.cjs`
@@ -138,7 +138,7 @@
* predicate this mirrors: that guard's marker parser tokenizes the whole
* line to rule out a marker surviving inside quoted argv text (a commit
* MESSAGE quoting the token). This guard's two detectors never process
* commit-message-shaped free text, so a plain `#\s*gsd-scan-ignore:` literal
* commit-message-shaped free text, so a plain `#\s*msd-scan-ignore:` literal
* match is sufficient here and is not widened to match that guard's
* quote-awareness it has no corresponding hazard for.
*
@@ -169,7 +169,7 @@
* regexes, which need no literal tokenizer — shared here only for the walk
* and the report sanitizer).
*
* Surfaces scanned (SCAN_DIRS): `gsd-core/workflows`, `commands`, `agents`,
* Surfaces scanned (SCAN_DIRS): `msd-core/workflows`, `commands`, `agents`,
* `skills` — the prompt-layer markdown that ships to every runtime.
* SCAN_EXT: `.md` only.
*
@@ -316,12 +316,12 @@ function detectGlobOperand(line) {
// ─── Escape marker ─────────────────────────────────────────────────────────
//
// `# gsd-scan-ignore: <reason>`. Two `\s*` quantifiers over fixed literals,
// `# msd-scan-ignore: <reason>`. Two `\s*` quantifiers over fixed literals,
// then a single trailing `(.*)$` — one quantifier, no nesting. Lines are
// split via `/\r?\n/` (see findUnreachableGuardDrift) before this ever runs,
// so `.` never has to reason about a trailing `\r` — the pitfall the CRLF
// coverage in the test matrix (P1-P4) exists to catch.
const MARKER_RE = /#\s*gsd-scan-ignore:\s*(.*)$/;
const MARKER_RE = /#\s*msd-scan-ignore:\s*(.*)$/;
// DELIBERATE DIVERGENCE from tests/commit-files-pathspec.test.cjs's own
// `ISSUE_REF_RE` (`/#\d+|https?:\/\//`), which this predicate started as a
@@ -348,7 +348,7 @@ const ISSUE_REF_RE = /#[1-9]\d*|https?:\/\/[^\s]+/;
// `scanTree` (scripts/lib/drift-scan.cjs) builds its repo-relative path via
// `path.relative()`, which uses NATIVE separators: on Windows that is
// `gsd-core\workflows\plan-phase.md`, while the committed baseline stores
// `msd-core\workflows\plan-phase.md`, while the committed baseline stores
// POSIX paths. Normalized UNCONDITIONALLY — never gated on
// `process.platform` — for the exact reason `lint-planning-prompt-drift.cjs`
// documents at its own `toPosixRel`: a platform-conditional normalizer is
@@ -359,7 +359,7 @@ function toPosixRel(relPath) {
}
// Prompt-layer markdown that ships to every runtime.
const SCAN_DIRS = ['gsd-core/workflows', 'commands', 'agents', 'skills'];
const SCAN_DIRS = ['msd-core/workflows', 'commands', 'agents', 'skills'];
const SCAN_EXT = new Set(['.md']);
const BASELINE_REL_PATH = path.join('scripts', 'baselines', 'unreachable-guard-drift-baseline.json');
@@ -389,7 +389,7 @@ const RATCHET_OWNER_ISSUE = '#3409';
* baseline JSON shape and the `--json` report shape are unchanged by
* Detector A's retirement.
* - `malformed`: `[{ file, line, text, reason }]` — an ATTEMPTED
* `# gsd-scan-ignore:` declaration whose reason names no issue and no
* `# msd-scan-ignore:` declaration whose reason names no issue and no
* URL. Checked on EVERY line independent of whether that line also
* matches a detector (a comment-only malformed declaration is still a
* malformed declaration) — never ratchet-eligible.
@@ -456,7 +456,7 @@ function scanRepo(root) {
* Frozen outcome-reason enum. CONTRIBUTING.md's "Prohibited: Raw Text
* Matching on Test Outputs" requires a typed structured surface wherever
* this module produces human-readable text — mirrors
* `gsd-core/bin/verify-reapply-patches.cjs`'s own `REASON` map exactly:
* `msd-core/bin/verify-reapply-patches.cjs`'s own `REASON` map exactly:
* `main()`'s `--json` mode and every `loadBaseline` per-error object carry
* one of these codes instead of free prose, and tests assert on the code,
* never on the rendered message. Adding a new reason requires updating this
@@ -713,11 +713,11 @@ function main() {
if (update) {
if (malformed.length > 0) {
if (!json) {
process.stderr.write('unreachable-guard-drift: malformed `# gsd-scan-ignore:` declaration(s) — fix these before regenerating the baseline (they are never ratchet-eligible):\n');
process.stderr.write('unreachable-guard-drift: malformed `# msd-scan-ignore:` declaration(s) — fix these before regenerating the baseline (they are never ratchet-eligible):\n');
for (const m of malformed) {
process.stderr.write(` ${sanitizeForReport(m.file)}:${m.line} ${sanitizeForReport(m.text)}\n`);
}
process.stderr.write('\n remedy: the reason after `# gsd-scan-ignore:` must name a tracking issue (#NNN) or an http(s):// URL.\n');
process.stderr.write('\n remedy: the reason after `# msd-scan-ignore:` must name a tracking issue (#NNN) or an http(s):// URL.\n');
}
emitJson({ reason: REASON.FAIL_MALFORMED_MARKER, violations: [], malformed, stale: [], baselineErrors: [] });
process.exitCode = 1;
@@ -768,7 +768,7 @@ function main() {
process.stderr.write('Detector B (cat/ls over a glob operand): under a nullglob set elsewhere in the same shell\n');
process.stderr.write('session, an unmatched glob reads from stdin (cat) or lists the cwd (ls) — use an array\n');
process.stderr.write('expansion or an existence test instead.\n');
process.stderr.write(`Or, if this is a deliberate wrong-example, declare it with # gsd-scan-ignore: #NNN, or add an\n`);
process.stderr.write(`Or, if this is a deliberate wrong-example, declare it with # msd-scan-ignore: #NNN, or add an\n`);
process.stderr.write(`acknowledged entry to ${BASELINE_REL_PATH} via --update:\n`);
for (const v of fresh) {
process.stderr.write(` ${sanitizeForReport(v.file)}:${v.line} [${v.kind}] ${sanitizeForReport(v.found)} ${sanitizeForReport(v.text)}\n`);
@@ -784,11 +784,11 @@ function main() {
}
if (malformed.length > 0) {
process.stderr.write('\nunreachable-guard-drift: malformed `# gsd-scan-ignore:` declaration(s) — never ratchet-eligible, must be fixed directly:\n');
process.stderr.write('\nunreachable-guard-drift: malformed `# msd-scan-ignore:` declaration(s) — never ratchet-eligible, must be fixed directly:\n');
for (const m of malformed) {
process.stderr.write(` ${sanitizeForReport(m.file)}:${m.line} ${sanitizeForReport(m.text)}\n`);
}
process.stderr.write('\n remedy: the reason after `# gsd-scan-ignore:` must name a tracking issue (#NNN) or an http(s):// URL.\n');
process.stderr.write('\n remedy: the reason after `# msd-scan-ignore:` must name a tracking issue (#NNN) or an http(s):// URL.\n');
}
}

View File

@@ -2,14 +2,14 @@
'use strict';
/**
* lint-vendored-deps.cjs — freshness gate for gsd-core/bin/lib/vendor/.
* lint-vendored-deps.cjs — freshness gate for msd-core/bin/lib/vendor/.
*
* #3477 follow-up: gsd-core/bin/** is copied by the installer into trees
* #3477 follow-up: msd-core/bin/** is copied by the installer into trees
* that have NO node_modules, so it must carry zero external requires
* (local/no-external-require-in-bin, eslint-rules/no-external-require-in-bin.cjs).
* Third-party packages that gsd-core/bin/** needs at runtime are instead
* vendored verbatim under gsd-core/bin/lib/vendor/ — see
* gsd-core/bin/lib/vendor/README.md.
* Third-party packages that msd-core/bin/** needs at runtime are instead
* vendored verbatim under msd-core/bin/lib/vendor/ — see
* msd-core/bin/lib/vendor/README.md.
*
* A vendored artifact that silently drifts from its upstream package is
* just as dangerous as never vendoring it in the first place (a stale
@@ -23,13 +23,13 @@
* 1. The vendored `.cjs` no longer matches its upstream `node_modules`
* build output byte-for-byte.
* 2. (upstream-verbatim twins only) The vendored `.d.cts` under
* gsd-core/bin/lib/vendor/ no longer matches its upstream
* msd-core/bin/lib/vendor/ no longer matches its upstream
* `node_modules` `.d.cts` byte-for-byte.
* 3. (upstream-verbatim twins only) The source-side twin under
* src/vendor/ (which tsc needs to resolve types for a relative
* `./vendor/<pkg>.cjs` import — module resolution for a .cts source
* is relative to src/, not the output dir) no longer matches the
* vendored `.d.cts` under gsd-core/bin/lib/vendor/.
* vendored `.d.cts` under msd-core/bin/lib/vendor/.
* 4. The package's version pinned in package.json `devDependencies` no
* longer matches the version actually installed at
* `node_modules/<pkg>/package.json` (read there, per the dispatch
@@ -82,10 +82,10 @@ function resolvePath(p) {
* @typedef {object} VendoredPackage
* @property {string} name npm package name, matches package.json devDependencies key
* @property {string} upstreamCjs path under node_modules/ to the upstream build artifact
* @property {string} vendoredCjs path under gsd-core/bin/lib/vendor/ to the vendored copy
* @property {string} vendoredCjs path under msd-core/bin/lib/vendor/ to the vendored copy
* @property {string|null} upstreamDts path under node_modules/ to the upstream .d.cts/.d.ts, or
* null when upstream ships no types (forces hand-authored)
* @property {string|null} vendoredDts path under gsd-core/bin/lib/vendor/ to the vendored .d.cts,
* @property {string|null} vendoredDts path under msd-core/bin/lib/vendor/ to the vendored .d.cts,
* or null when there is no bin-side type twin
* @property {string|null} srcTwin path under src/vendor/ to the source-side type twin tsc
* resolves for a relative import from src/**, or null
@@ -102,16 +102,16 @@ const VENDORED = [
{
name: 're2js',
upstreamCjs: 'node_modules/re2js/build/index.cjs',
vendoredCjs: 'gsd-core/bin/lib/vendor/re2js.cjs',
vendoredCjs: 'msd-core/bin/lib/vendor/re2js.cjs',
upstreamDts: 'node_modules/re2js/build/index.d.cts',
vendoredDts: 'gsd-core/bin/lib/vendor/re2js.d.cts',
vendoredDts: 'msd-core/bin/lib/vendor/re2js.d.cts',
srcTwin: 'src/vendor/re2js.d.cts',
twinKind: 'upstream-verbatim',
},
{
name: 'js-yaml',
upstreamCjs: 'node_modules/js-yaml/dist/js-yaml.js',
vendoredCjs: 'gsd-core/bin/lib/vendor/js-yaml.cjs',
vendoredCjs: 'msd-core/bin/lib/vendor/js-yaml.cjs',
upstreamDts: null,
vendoredDts: null,
srcTwin: 'src/vendor/js-yaml.d.cts',
@@ -352,7 +352,7 @@ function main() {
if (Object.keys(remaining).length === 0) {
process.stdout.write(
`ok lint-vendored-deps --fix: gsd-core/bin/lib/vendor/{${names}} refreshed and now match node_modules and their pinned versions\n`,
`ok lint-vendored-deps --fix: msd-core/bin/lib/vendor/{${names}} refreshed and now match node_modules and their pinned versions\n`,
);
return 0;
}
@@ -379,7 +379,7 @@ function main() {
const names = VENDORED.map((row) => row.name).join(', ');
throw new ExitError(
1,
`lint-vendored-deps: gsd-core/bin/lib/vendor/{${names}} has drifted from its\n`
`lint-vendored-deps: msd-core/bin/lib/vendor/{${names}} has drifted from its\n`
+ 'upstream package (or its version pin). Refresh with:\n'
+ ` ${REFRESH_COMMAND}\n`
+ 'Findings:\n'
@@ -388,7 +388,7 @@ function main() {
}
const names = VENDORED.map((row) => row.name).join(', ');
process.stdout.write(`ok lint-vendored-deps: gsd-core/bin/lib/vendor/{${names}} match node_modules and their pinned versions\n`);
process.stdout.write(`ok lint-vendored-deps: msd-core/bin/lib/vendor/{${names}} match node_modules and their pinned versions\n`);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,8 @@
* lint-workflow-shellcheck.cjs
*
* Systemic prevention for the zsh/bash word-splitting bug class (#4109):
* every ```bash fenced block embedded in gsd-core/workflows/*.md (and the
* nested gsd-core/workflows/<workflow>/steps/*.md / modes/*.md / etc. layer)
* every ```bash fenced block embedded in msd-core/workflows/*.md (and the
* nested msd-core/workflows/<workflow>/steps/*.md / modes/*.md / etc. layer)
* is extracted and run through the real ShellCheck binary. Any finding fails
* the lint with a non-zero exit — this is what stops the SC2086-class bug
* (unquoted variable expansion, word-split/glob differently under zsh vs
@@ -122,8 +122,8 @@ const { resolveShellcheckBin } = require('./lib/shellcheck-fetch.cjs');
const SHELLCHECK_TIMEOUT_MS = 60_000;
const ROOT = path.join(__dirname, '..');
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
const SECTIONIZER_PATH = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'markdown-sectionizer.cjs');
const WORKFLOWS_DIR = path.join(ROOT, 'msd-core', 'workflows');
const SECTIONIZER_PATH = path.join(ROOT, 'msd-core', 'bin', 'lib', 'markdown-sectionizer.cjs');
const BASELINE_PATH = path.join(__dirname, 'lint-workflow-shellcheck-baseline.json');
// Codes excluded for structural reasons documented in the module header above.
@@ -492,7 +492,7 @@ async function main() {
const blocks = extractBashBlocks(sectionizer);
if (blocks.length === 0) {
process.stdout.write('ok lint-workflow-shellcheck: no ```bash blocks found under gsd-core/workflows/\n');
process.stdout.write('ok lint-workflow-shellcheck: no ```bash blocks found under msd-core/workflows/\n');
return 0;
}
@@ -524,7 +524,7 @@ async function main() {
const shellcheckBin = await resolveShellcheckBin();
const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-workflow-shellcheck-'));
const stageDir = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-workflow-shellcheck-'));
try {
const stagedPaths = [];
const byPath = new Map();
@@ -571,7 +571,7 @@ async function main() {
process.stderr.write(
`\nERROR lint-workflow-shellcheck: ${newFindings.length} NEW ShellCheck finding(s) in ` +
`gsd-core/workflows/ \`\`\`bash block(s) (#4109 word-splitting/quoting prevention) not present in ` +
`msd-core/workflows/ \`\`\`bash block(s) (#4109 word-splitting/quoting prevention) not present in ` +
`${path.relative(ROOT, BASELINE_PATH)}.\n\n`,
);
for (const f of newFindings) {

View File

@@ -16,7 +16,7 @@
* So the class is silent by construction: it damages the developer's machine and
* reports nothing. #2665 records two prior authors each diagnosing it and fixing
* only the instance in front of them. This module converts it from silent to
* loud by snapshotting GSD's own install footprint before the suite and
* loud by snapshotting MSD's own install footprint before the suite and
* re-checking it after.
*
* LOCATION — `scripts/`, deliberately NOT `scripts/lib/`. The installer copies
@@ -28,9 +28,9 @@
* run-affected-tests.cjs — excluding one link alone would trip the #2858
* shipped-requires-only-shipped gate on the links that still shipped).
*
* SCOPE — ownership-based, not whole-root. It watches entries GSD unambiguously
* owns: the top-level install footprint (`GSD_OWNED_ENTRIES`) plus `gsd-`-prefixed
* children of the dirs GSD shares with the host agent (`GSD_PREFIXED_PARENTS`).
* SCOPE — ownership-based, not whole-root. It watches entries MSD unambiguously
* owns: the top-level install footprint (`MSD_OWNED_ENTRIES`) plus `msd-`-prefixed
* children of the dirs MSD shares with the host agent (`MSD_PREFIXED_PARENTS`).
* It does NOT watch whole config roots. A root such as `~/.claude` is shared with
* the host agent, which may legitimately write `history.jsonl`, `todos/`, or
* `settings.json` while the suite runs; watching the root would turn that into a
@@ -39,21 +39,21 @@
*
* The ownership test is the prefix, not the location. That distinction is load
* bearing: the first version of this guard watched only the three top-level
* entries and MISSED a real leak into `<live>/skills/gsd-dev-preferences/`.
* entries and MISSED a real leak into `<live>/skills/msd-dev-preferences/`.
*
* KNOWN GAP — a leak into a file GSD does not own (e.g. mutating the host's own
* KNOWN GAP — a leak into a file MSD does not own (e.g. mutating the host's own
* `.claude.json`, `settings.json`, `hooks.json`, `kilo.json`, `opencode.json`) is
* outside this guard by construction. Closing it would require watching shared
* files, which is the false-positive trap above.
*
* NAMED RESIDUALS — stated rather than implied, because two successive rounds
* asserted this list was complete and both were refuted. Still NOT watched:
* - `agents/subagents/**` (kimi stages `subagents/gsd-executor.yaml` under an
* - `agents/subagents/**` (kimi stages `subagents/msd-executor.yaml` under an
* UNPREFIXED intermediate dir, so no prefix scan of `agents/` reaches it);
* - the loose capability generators copied to `<root>/scripts/*.cjs`
* (`fix-slash-commands.cjs` is watched by name; the generators are not);
* - `extensions/package.json` and `plugins/package.json` — CommonJS markers in
* dirs GSD fills but does not own, so they fall under the shared-ground rule
* dirs MSD fills but does not own, so they fall under the shared-ground rule
* below rather than being watched;
* - the shared-hooks bundle in a NON-registry root's `<root>/hooks/` (kimi) —
* see resolveExtraWatchTargets; closing it is a layout decision.
@@ -64,10 +64,10 @@
* Every item above under-watches, which fails quiet: a missed leak, never a
* false alarm.
*
* SEVERITY — reports by default, fails only under GSD_STRICT_LIVE_CONFIG_GUARD=1.
* SEVERITY — reports by default, fails only under MSD_STRICT_LIVE_CONFIG_GUARD=1.
* Not timidity: on its first CI run this guard found PRE-EXISTING leaks on the
* Windows lane (`C:\Users\runneradmin\.claude\gsd-core` and
* `skills\gsd-dev-preferences`), because os.homedir() reads USERPROFILE there and
* Windows lane (`C:\Users\runneradmin\.claude\msd-core` and
* `skills\msd-dev-preferences`), because os.homedir() reads USERPROFILE there and
* ~190 test sites sandbox HOME alone. Those are real and worth fixing, but they
* are a different defect class from the one #2665 closes, and a brand-new gate
* that immediately reds an unrelated lane gets bypassed or reverted rather than
@@ -81,40 +81,40 @@ const os = require('os');
const path = require('path');
/**
* Top-level entries only a GSD install creates. See SCOPE above before widening.
* Top-level entries only a MSD install creates. See SCOPE above before widening.
*
* `.gsd-source` and `.gsd-profile` were added by the round-5 census (see below):
* `.msd-source` and `.msd-profile` were added by the round-5 census (see below):
* bin/install.js writes both at the config ROOT for a global install, and an
* exact-name list does not match a dot-prefixed name by the `gsd-` prefix rule.
* exact-name list does not match a dot-prefixed name by the `msd-` prefix rule.
*/
const GSD_OWNED_ENTRIES = [
'gsd-core',
'gsd-file-manifest.json',
'gsd-pristine',
'.gsd-source',
'.gsd-profile',
const MSD_OWNED_ENTRIES = [
'msd-core',
'msd-file-manifest.json',
'msd-pristine',
'.msd-source',
'.msd-profile',
];
/**
* Directories GSD SHARES with the host agent. Watching them wholesale would
* false-positive on the host's own writes, so only `gsd-`-prefixed children are
* Directories MSD SHARES with the host agent. Watching them wholesale would
* false-positive on the host's own writes, so only `msd-`-prefixed children are
* watched — those are unambiguously ours.
*
* Added after the first version of this guard MISSED a real leak: a raw
* `spawnSync` that sandboxed HOME but inherited an ambient CLAUDE_CONFIG_DIR
* wrote `<live>/skills/gsd-dev-preferences/SKILL.md`, which sits under none of
* wrote `<live>/skills/msd-dev-preferences/SKILL.md`, which sits under none of
* the three top-level entries above.
*
* `hooks` joined them in round 5, found by re-deriving the census rather than by
* a review finding — the SAME shape one parent over. bin/install.js writes
* `hooks/gsd-check-update.js`, `hooks/gsd-context-monitor.js` and
* `hooks/gsd-update-banner.js` into the config root, and with `hooks` absent from
* `hooks/msd-check-update.js`, `hooks/msd-context-monitor.js` and
* `hooks/msd-update-banner.js` into the config root, and with `hooks` absent from
* this list a leak of any of them passed the guard silently. The lesson the first
* miss taught is that this list is the weak point, so it is re-derived from the
* installer's own write sites each round rather than trusted.
*/
const GSD_PREFIXED_PARENTS = ['agents', 'commands', 'skills', 'hooks'];
const GSD_ARTIFACT_PREFIX = 'gsd-';
const MSD_PREFIXED_PARENTS = ['agents', 'commands', 'skills', 'hooks'];
const MSD_ARTIFACT_PREFIX = 'msd-';
/**
* Artifact parents that are NOT registry-declared — the installer writes these
@@ -124,14 +124,14 @@ const NON_REGISTRY_ARTIFACT_PARENTS = ['hooks', 'plugins', 'scripts', 'extension
/**
* Prefixes used for a parent with no registry-declared one. BOTH forms are the
* point: GSD writes `gsd-`-hyphen artifacts (`hooks/gsd-check-update.js`) AND
* bare `gsd.`-dotted ones (pi's `extensions/gsd.js`), and a lone `gsd-` sees
* point: MSD writes `msd-`-hyphen artifacts (`hooks/msd-check-update.js`) AND
* bare `msd.`-dotted ones (pi's `extensions/msd.js`), and a lone `msd-` sees
* only the first.
*/
const DEFAULT_ARTIFACT_PREFIXES = ['gsd-', 'gsd.'];
const DEFAULT_ARTIFACT_PREFIXES = ['msd-', 'msd.'];
/**
* Files GSD owns by EXACT NAME inside a directory it shares — deliberately NOT
* Files MSD owns by EXACT NAME inside a directory it shares — deliberately NOT
* the directories themselves.
*
* `hooks/lib`, `hooks/package.json`, `scripts/lib` and `scripts/changeset` were
@@ -142,7 +142,7 @@ const DEFAULT_ARTIFACT_PREFIXES = ['gsd-', 'gsd.'];
* helper into a violation, which is the false-positive trap the SCOPE note above
* exists to refuse. Under-watching fails quiet; over-watching disarms the guard.
*/
const GSD_OWNED_NESTED = [
const MSD_OWNED_NESTED = [
'scripts/fix-slash-commands.cjs',
'hooks/managed-hooks-registry.cjs',
];
@@ -153,13 +153,13 @@ const GSD_OWNED_NESTED = [
* ever as complete as its author's recall, and this one was measurably not.
*
* Round 5's adversarial review found the hand-list missing Kilo's SINGULAR
* `command/`, `workflows/`, and Hermes' `skills/gsd` -- the last being a whole
* directory whose name carries no `gsd-` prefix, so no prefix rule reaches it.
* `command/`, `workflows/`, and Hermes' `skills/msd` -- the last being a whole
* directory whose name carries no `msd-` prefix, so no prefix rule reaches it.
* A capability that declares a new destSubpath now extends this set in the same
* commit that declares it.
*
* @returns {{parents: string[], owned: string[]}} parents = watch gsd-prefixed
* children only; owned = watch the path wholesale (its own name is GSD's).
* @returns {{parents: string[], owned: string[]}} parents = watch msd-prefixed
* children only; owned = watch the path wholesale (its own name is MSD's).
*/
function deriveArtifactTargets(runtimes) {
const parents = new Map();
@@ -167,21 +167,21 @@ function deriveArtifactTargets(runtimes) {
if (!parents.has(dest)) parents.set(dest, new Set());
for (const pre of prefixes) parents.get(dest).add(pre);
};
for (const dest of [...GSD_PREFIXED_PARENTS, ...NON_REGISTRY_ARTIFACT_PARENTS]) {
for (const dest of [...MSD_PREFIXED_PARENTS, ...NON_REGISTRY_ARTIFACT_PARENTS]) {
addParent(dest, DEFAULT_ARTIFACT_PREFIXES);
}
const owned = new Set(GSD_OWNED_NESTED);
const owned = new Set(MSD_OWNED_NESTED);
for (const entry of Object.values(runtimes || {})) {
for (const layout of entry?.runtime?.artifactLayout?.global ?? []) {
const dest = layout?.destSubpath;
if (typeof dest !== 'string' || !dest) continue;
const last = dest.split('/').pop() || '';
// A destination whose own final segment is GSD's (hermes' `skills/gsd`) is
// A destination whose own final segment is MSD's (hermes' `skills/msd`) is
// owned wholesale — that directory is ours, not shared.
if (last.startsWith('gsd')) { owned.add(dest); continue; }
if (last.startsWith('msd')) { owned.add(dest); continue; }
// The layout declares its OWN prefix, and it varies: kimi's `kimi-agents`
// layout declares `gsd` (no hyphen) and writes `agents/gsd.yaml` +
// `agents/gsd.md`, invisible to a fixed `gsd-` scan. The same destSubpath
// layout declares `msd` (no hyphen) and writes `agents/msd.yaml` +
// `agents/msd.md`, invisible to a fixed `msd-` scan. The same destSubpath
// also carries different prefixes across runtimes, so a parent maps to a SET.
const declared = typeof layout?.prefix === 'string' && layout.prefix
? [layout.prefix]
@@ -198,7 +198,7 @@ function deriveArtifactTargets(runtimes) {
let _artifactTargets = null;
function artifactTargets(deps = {}) {
if (_artifactTargets && !deps.libDir) return _artifactTargets;
const libDir = deps.libDir || path.join(__dirname, '..', 'gsd-core', 'bin', 'lib');
const libDir = deps.libDir || path.join(__dirname, '..', 'msd-core', 'bin', 'lib');
let runtimes;
try {
({ runtimes } = require(path.join(libDir, 'capability-registry.cjs')));
@@ -212,10 +212,10 @@ function artifactTargets(deps = {}) {
}
/**
* The file GSD writes into a NON-REGISTRY config home.
* The file MSD writes into a NON-REGISTRY config home.
*
* Both current descriptors are Kimi's — Kimi CLI's `~/.kimi` (KIMI_SHARE_DIR) and
* Kimi Code's `~/.kimi-code` (KIMI_CODE_HOME) — and GSD writes its native
* Kimi Code's `~/.kimi-code` (KIMI_CODE_HOME) — and MSD writes its native
* `[[hooks]]` block into `config.toml` in each, so the single filename below holds
* for both. NAMED RESIDUAL: this assumes every non-registry descriptor is written
* the same way. That assumption is now load-bearing rather than vacuous — it is
@@ -233,7 +233,7 @@ const NON_REGISTRY_OWNED_FILE = 'config.toml';
* budget threaded across every target, which made the guard's verdict depend on
* directory ORDER and on unrelated local state: one large early target exhausted
* it, and every target scanned afterwards reported `truncated` -> `unverified`,
* which under GSD_STRICT_LIVE_CONFIG_GUARD=1 is a failed run. Per-target means a
* which under MSD_STRICT_LIVE_CONFIG_GUARD=1 is a failed run. Per-target means a
* pathological tree truncates ITSELF and nothing else.
*
* MAX_TOTAL_ENTRIES keeps the aggregate bounded, which is what the single budget
@@ -267,7 +267,7 @@ const MAX_DEPTH = 12;
* @returns {string[]} deduped, sorted roots; empty if the built lib is absent.
*/
function resolveLiveConfigRoots(deps = {}) {
const libDir = deps.libDir || path.join(__dirname, '..', 'gsd-core', 'bin', 'lib');
const libDir = deps.libDir || path.join(__dirname, '..', 'msd-core', 'bin', 'lib');
const homedir = (deps.os || os).homedir;
let getGlobalConfigDir;
let resolveConfigHomeFromDescriptor;
@@ -316,10 +316,10 @@ function resolveLiveConfigRoots(deps = {}) {
//
// DELIBERATE NON-ROOT: getGlobalSkillsBase(runtime) is NOT added here. The
// skills base (e.g. codex's ~/.agents/skills) is not a config ROOT, and the
// snapshot applies the config-root layout (GSD_OWNED_ENTRIES x
// GSD_PREFIXED_PARENTS) beneath every root it is given — measured on a
// sandboxed HOME, adding it both false-positives on `<skillsBase>/gsd-core`
// and misses a real `<skillsBase>/gsd-help` write. Watching skills bases
// snapshot applies the config-root layout (MSD_OWNED_ENTRIES x
// MSD_PREFIXED_PARENTS) beneath every root it is given — measured on a
// sandboxed HOME, adding it both false-positives on `<skillsBase>/msd-core`
// and misses a real `<skillsBase>/msd-help` write. Watching skills bases
// needs its own layout, like resolveExtraWatchTargets — a separate change.
for (const runtime of [...Object.keys(runtimes || {}), 'grok']) {
try {
@@ -334,26 +334,26 @@ function resolveLiveConfigRoots(deps = {}) {
/**
* Watch targets that are NOT runtime config roots, and so cannot be expressed as
* `root x GSD_OWNED_ENTRIES`.
* `root x MSD_OWNED_ENTRIES`.
*
* #2665 round 3: resolveLiveConfigRoots enumerates getGlobalConfigDir per registry
* runtime plus grok. A live write surface that is not a config ROOT is invisible to
* that shape, so a leak on one passed through this guard — the PR's own safety net —
* silently. There are THREE today ($GSD_HOME/.gsd, plus one config.toml per entry in
* silently. There are THREE today ($MSD_HOME/.msd, plus one config.toml per entry in
* NON_REGISTRY_CONFIG_HOME_DESCRIPTORS, which #2755 took from one entry to two):
*
* $GSD_HOME/.gsd — GSD's user-owned store (consent.json, defaults.json, capability
* $MSD_HOME/.msd — MSD's user-owned store (consent.json, defaults.json, capability
* overlays). Watched WHOLESALE: unlike ~/.claude this root is
* exclusively ours, so the shared-root false-positive trap in
* SCOPE above does not apply and an ownership filter would only
* narrow the guard for nothing.
* <non-registry home>/config.toml — the file GSD writes its native [[hooks]] block
* <non-registry home>/config.toml — the file MSD writes its native [[hooks]] block
* into, one per NON_REGISTRY_CONFIG_HOME_DESCRIPTORS entry: Kimi
* CLI's ~/.kimi (KIMI_SHARE_DIR) and, since #2755, Kimi Code's
* ~/.kimi-code (KIMI_CODE_HOME). The INVERSE case: those roots
* belong to their products, so the root is never watched
* wholesale. This is the KNOWN GAP above accepted deliberately
* in one direction — GSD demonstrably writes these files
* in one direction — MSD demonstrably writes these files
* (bin/install.js resolves the hooks-toml dir at two sites), so
* a concurrent write by those products is the only false
* positive, and neither runs during the suite. NOT watched, and
@@ -365,7 +365,7 @@ function resolveLiveConfigRoots(deps = {}) {
* @returns {string[]} absolute paths; empty if the built lib is absent.
*/
function resolveExtraWatchTargets(deps = {}) {
const libDir = deps.libDir || path.join(__dirname, '..', 'gsd-core', 'bin', 'lib');
const libDir = deps.libDir || path.join(__dirname, '..', 'msd-core', 'bin', 'lib');
const env = deps.env || process.env;
const homedir = (deps.os || os).homedir;
@@ -374,8 +374,8 @@ function resolveExtraWatchTargets(deps = {}) {
// path leaves the fallback unwatched, which is the same defect B3 closed for the
// registry roots; it lived here too until round 5's adversarial review found it.
const targets = [
path.resolve(path.join(env.GSD_HOME || homedir(), '.gsd')),
path.resolve(path.join(homedir(), '.gsd')),
path.resolve(path.join(env.MSD_HOME || homedir(), '.msd')),
path.resolve(path.join(homedir(), '.msd')),
];
try {
@@ -406,9 +406,9 @@ function resolveExtraWatchTargets(deps = {}) {
// the named file below.
//
// NAMED RESIDUAL (#2665, found pre-push while rebasing): config.toml is NOT
// the only thing GSD writes here. bin/install.js also calls
// the only thing MSD writes here. bin/install.js also calls
// installSharedHooksBundle(kimiHooksRoot), which populates <root>/hooks/
// with GSD's hook scripts and a CommonJS marker. That subtree is UNWATCHED,
// with MSD's hook scripts and a CommonJS marker. That subtree is UNWATCHED,
// so a suite-produced leak of a hook bundle into a developer's real ~/.kimi
// or ~/.kimi-code passes this guard silently. Closing it needs a layout
// decision, not one more path: the same reason getGlobalSkillsBase is a
@@ -465,7 +465,7 @@ function newestMtime(target, budget) {
}
/**
* Snapshot GSD-owned entries under each root.
* Snapshot MSD-owned entries under each root.
*
* @returns {Record<string, {exists: boolean, newest: number, truncated: boolean}>}
* keyed by absolute entry path.
@@ -500,18 +500,18 @@ function snapshotLiveConfig(roots, extraTargets = [], limits = {}) {
// Non-root targets (resolveExtraWatchTargets) are recorded verbatim — they are
// already the exact path to watch, whole-dir or single-file. Passed explicitly
// rather than resolved here so a caller testing a fixture root does not silently
// pull the developer's real ~/.gsd into its snapshot.
// pull the developer's real ~/.msd into its snapshot.
for (const target of extraTargets) record(path.resolve(target));
const { parents: watchParents, owned: watchOwned } = artifactTargets();
for (const root of roots) {
for (const entry of GSD_OWNED_ENTRIES) record(path.join(root, entry));
// Paths whose own name is GSD's, nested inside a shared root (hermes'
// `skills/gsd`, `hooks/lib`, `scripts/lib`, …) — no prefix rule sees these.
for (const entry of MSD_OWNED_ENTRIES) record(path.join(root, entry));
// Paths whose own name is MSD's, nested inside a shared root (hermes'
// `skills/msd`, `hooks/lib`, `scripts/lib`, …) — no prefix rule sees these.
for (const entry of watchOwned) record(path.join(root, ...entry.split('/')));
// Shared dirs: enumerate only gsd-prefixed children. A child that appears
// Shared dirs: enumerate only msd-prefixed children. A child that appears
// between the two snapshots is absent from `before` entirely — diffLiveConfig
// treats after-only paths as created, which is exactly the leak signal.
for (const [parent, prefixes] of Object.entries(watchParents)) {
@@ -537,10 +537,10 @@ function snapshotLiveConfig(roots, extraTargets = [], limits = {}) {
* Iterate the UNION of both key sets, never `after` alone. Deletion reaches this
* function in two shapes and an after-only walk sees neither:
*
* - A FIXED owned entry (GSD_OWNED_ENTRIES x roots, and every extra target) is
* - A FIXED owned entry (MSD_OWNED_ENTRIES x roots, and every extra target) is
* recorded at both ends whether or not it exists, so a deletion reads
* {exists:true} -> {exists:false} and falls through every branch — silently.
* - A gsd-prefixed child is DISCOVERED by readdir, so a deleted one is absent
* - A msd-prefixed child is DISCOVERED by readdir, so a deleted one is absent
* from `after` entirely and never enters an after-keyed loop at all.
*
* The second shape is why adding a `pre.exists && !post.exists` branch is not on
@@ -555,7 +555,7 @@ function diffLiveConfig(before, after) {
for (const target of targets) {
const pre = before[target];
const post = after[target];
// Absent from `before` entirely: a gsd-prefixed child that did not exist
// Absent from `before` entirely: a msd-prefixed child that did not exist
// when the run started. Both snapshots cover the same roots, so an
// after-only path was created BY the run — never skip it.
if (!pre) {
@@ -608,25 +608,25 @@ function formatViolations(violations) {
// failure captioned "Reporting only" sends the reader away from the very
// violation that just reddened their run.
lines.push(
process.env.GSD_STRICT_LIVE_CONFIG_GUARD === '1'
? 'STRICT MODE (GSD_STRICT_LIVE_CONFIG_GUARD=1): these violations fail the run. ' +
'GSD_SKIP_LIVE_CONFIG_GUARD=1 skips the check entirely.'
: 'Reporting only. Set GSD_STRICT_LIVE_CONFIG_GUARD=1 to make this fail the run, ' +
'or GSD_SKIP_LIVE_CONFIG_GUARD=1 to skip the check entirely.',
process.env.MSD_STRICT_LIVE_CONFIG_GUARD === '1'
? 'STRICT MODE (MSD_STRICT_LIVE_CONFIG_GUARD=1): these violations fail the run. ' +
'MSD_SKIP_LIVE_CONFIG_GUARD=1 skips the check entirely.'
: 'Reporting only. Set MSD_STRICT_LIVE_CONFIG_GUARD=1 to make this fail the run, ' +
'or MSD_SKIP_LIVE_CONFIG_GUARD=1 to skip the check entirely.',
);
lines.push('');
return lines.join('\n');
}
module.exports = {
GSD_OWNED_ENTRIES,
GSD_PREFIXED_PARENTS,
GSD_OWNED_NESTED,
MSD_OWNED_ENTRIES,
MSD_PREFIXED_PARENTS,
MSD_OWNED_NESTED,
NON_REGISTRY_ARTIFACT_PARENTS,
DEFAULT_ARTIFACT_PREFIXES,
deriveArtifactTargets,
artifactTargets,
GSD_ARTIFACT_PREFIX,
MSD_ARTIFACT_PREFIX,
MAX_ENTRIES,
MAX_TOTAL_ENTRIES,
MAX_DEPTH,

Some files were not shown because too many files have changed in this diff Show More