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:
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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 [];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": []
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -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/',
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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/',
|
||||
];
|
||||
|
||||
@@ -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) });
|
||||
|
||||
@@ -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) });
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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/')
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 —
|
||||
|
||||
@@ -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 [];
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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. |
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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>';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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' },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:',
|
||||
'',
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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").
|
||||
*
|
||||
|
||||
@@ -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",
|
||||
})
|
||||
]);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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' +
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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' +
|
||||
|
||||
@@ -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).`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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`,
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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$/;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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('/');
|
||||
|
||||
@@ -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.',
|
||||
],
|
||||
]),
|
||||
],
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user