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

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

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

View File

@@ -1,2 +1,2 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
console.log('sample brownfield source file');

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Architecture
Single-file CLI entry point, no layering.

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Concerns
None recorded.

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Conventions
No conventions established yet.

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Integrations
None.

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Stack
Node.js, plain CommonJS, no framework.

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Structure
`src/` holds the single entry point.

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Testing
No test suite exists yet.

View File

@@ -3,7 +3,7 @@ phase: "04"
plan: "04-01"
created: 2026-01-01
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Plan 04-01: Post-Rollover Feature

View File

@@ -3,7 +3,7 @@ phase: "02"
plan: "02-01"
created: 2026-01-01
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Plan 02-01: Printable Renderer

View File

@@ -3,7 +3,7 @@ phase: "01"
plan: "01-01"
created: 2026-01-01
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Plan 01-01: Tokenizer

View File

@@ -3,7 +3,7 @@ phase: "01"
plan: "01-01"
created: 2026-01-01
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Plan 01-01: Tokenizer

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Loop QA Walk Sample
## What This Is

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- Used by scenarios/milestone-rollover.json: stands in for the roadmapper's re-plan after
`milestone complete 1.0` — a real ROADMAP.md carries the next milestone's version marker,
which is what lets a boundary-aware oracle actually SEE the milestone_version change

View File

@@ -1,4 +1,4 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Roadmap: Loop QA Walk Sample
## Overview

View File

@@ -1,2 +1,2 @@
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
executor-01-01-a1b2c3d4

View File

@@ -5,7 +5,7 @@ current_plan: "01-01"
total_plans_in_phase: "1"
status: "In Progress"
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# STATE

View File

@@ -3,7 +3,7 @@ phase: "01"
plan: "01-01"
status: complete
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Plan 01-01 Summary

View File

@@ -3,7 +3,7 @@ phase: "01"
plan: "01-01"
status: in-progress
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Plan 01-01 Summary (PARTIAL — agent abandoned mid-execute)

View File

@@ -4,7 +4,7 @@ name: "Parser"
created: 2026-01-01
status: failed
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Phase 1: Parser — User Acceptance Testing

View File

@@ -4,7 +4,7 @@ name: "Parser"
created: 2026-01-01
status: passed
---
<!-- provenance: template-derived (gsd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
<!-- provenance: template-derived (msd-core/templates/). NOT real-user-sourced. See #2371 — adequate for happy-path/sequence scenarios, NOT sufficient as a negative fixture asserting the engine correctly rejects input. -->
# Phase 1: Parser — User Acceptance Testing

View File

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

View File

@@ -93,7 +93,7 @@ const SENTINEL_STRINGS = new Set(['undefined', 'null', 'NaN', '[object Object]']
* `checkAgentsInstalled`, surfaced on every `init` subcommand's response via
* `init.cts`'s `withProjectRoot`. Points at the INSTALL tree (the runtime's
* global config dir, or — for the `claude` runtime — the `agents/` directory
* bundled as a sibling of `gsd-core/`), never at the project: agents are
* bundled as a sibling of `msd-core/`), never at the project: agents are
* installed once, not per-project.
*/
const EXTERNAL_PATH_ALLOWED_KEYS = Object.freeze(new Set(['agents_dir']));
@@ -334,11 +334,11 @@ const ORACLES = Object.freeze([
'(VIOLATION). When ctx.projectDir is supplied, an absolute-path string outside it is reported as a SMELL — ' +
'never a violation — UNLESS its leaf key name is in EXTERNAL_PATH_ALLOWED_KEYS (a field whose contract is to ' +
'point outside the project, e.g. agents_dir) or the string\'s FIRST WHITESPACE-DELIMITED WORD is an EXACT ' +
'member of getLiveCommandTokens() (e.g. `/gsd:progress`, or `/gsd-plan-phase 2` where the token carries ' +
'member of getLiveCommandTokens() (e.g. `/msd:progress`, or `/msd-plan-phase 2` where the token carries ' +
'trailing arguments — see isLiveCommandToken in tests/helpers/live-command-registry.cjs, the single predicate ' +
'shared with routing-validity), either of which is skipped entirely: not a smell, not a violation. Exact ' +
'membership, never a `startsWith` prefix test with an unconstrained remainder — a string merely SHARING a ' +
'prefix with a real token (e.g. `/gsd-x/../../../etc/passwd`) is not exempted and still surfaces as a SMELL. ' +
'prefix with a real token (e.g. `/msd-x/../../../etc/passwd`) is not exempted and still surfaces as a SMELL. ' +
'Without ctx.projectDir the path check is skipped rather than guessed.',
check(ctx) {
try {
@@ -555,14 +555,14 @@ const ORACLES = Object.freeze([
'Every command token actually carried by the payload must name a command present in ctx.liveCommands. ' +
'Rather than enumerating fixed field paths (`actions[].command` / `next.command`), this walks the ENTIRE ' +
'payload for any string whose first whitespace-delimited word starts with one of LIVE_COMMAND_TOKEN_PREFIXES ' +
'(`/gsd-`, `/gsd:`, `$gsd-`) — catching the token wherever it is actually carried: `recommended_command`, a ' +
'(`/msd-`, `/msd:`, `$msd-`) — catching the token wherever it is actually carried: `recommended_command`, a ' +
'bare-string `next`, `next` as an array of `{command}`, `actions` as an object map, `steps[].command`, ' +
'`actions[].next.command`, etc (#3913 P9 SEC-2 — the fixed-path version missed all of these). A candidate ' +
'passes only when its first word is an EXACT member of ctx.liveCommands (via the shared ' +
'`isLiveCommandToken` predicate, tests/helpers/live-command-registry.cjs — the SAME predicate ' +
'value-hygiene\'s command-token exemption uses, so the two can never drift on what a live token is); a ' +
'pleasing consequence is that value-hygiene therefore exempts exactly the strings routing-validity vouches ' +
'for. A bare `recommended` id (no `/gsd-`-style prefix — e.g. `discuss-phase`, by design ' +
'for. A bare `recommended` id (no `/msd-`-style prefix — e.g. `discuss-phase`, by design ' +
'`src/smart-entry.cts` `actions.find(a => a.recommended)?.id`, an action id, not a command token) is never a ' +
'candidate. Engages only when at least one prefix-shaped string is present; a payload with no such string is ' +
'not engaged (ok).',

View File

@@ -83,7 +83,7 @@ function realpathNearestAncestor(candidate, realpathFn = defaultRealpath) {
* MACOS SYMLINK CASE (do not "simplify" this back to a lexical compare): on macOS,
* `os.tmpdir()` / `TMPDIR` resolves to `/var/folders/...`, which is itself a symlink to
* `/private/var/folders/...`. `fs.mkdtempSync` returns the unresolved `/var/...` form, but
* `gsd-tools` output paths are realpath-resolved by the OS/child process to the
* `msd-tools` output paths are realpath-resolved by the OS/child process to the
* `/private/var/...` form. Comparing those two strings *lexically* makes the very same
* directory compare as "outside the project" and fires a spurious `value-hygiene` smell on
* every macOS run — and would make a legitimate `resolveWithin` call throw a false containment

View File

@@ -21,7 +21,7 @@
* different documents, which defeats diffing/reviewing a report in CI.
*
* REPRO LINES ARE ALWAYS STRINGS: `step.repro` is either a real,
* copy-pasteable `cd <dir> && node <absolute path to gsd-tools.cjs> ...`
* copy-pasteable `cd <dir> && node <absolute path to msd-tools.cjs> ...`
* command (plus a trailing `#` shell-comment note about the pinned clock and
* cleared env — see `buildRepro`), or a string clearly prefixed
* `NOT RUNNABLE: ...` explaining why (the tree was not preserved, or the
@@ -42,7 +42,7 @@ const REPORT_VERSION = 1;
*
* The emitted `node` path is ABSOLUTE (resolved from this file's own
* location via `__dirname`), never the repo-relative
* `gsd-core/bin/gsd-tools.cjs` — `preservedDir` is a temp project directory
* `msd-core/bin/msd-tools.cjs` — `preservedDir` is a temp project directory
* unrelated to the repo checkout, so `cd`ing into it first and then
* resolving a repo-relative path throws `MODULE_NOT_FOUND` (#3597).
*
@@ -54,16 +54,16 @@ function buildRepro({ preservedDir, argv }) {
return 'NOT RUNNABLE: this step declared no CLI invocation (no "run" array) — there is nothing to reproduce.';
}
if (!preservedDir) {
return 'NOT RUNNABLE: the scenario tree was not preserved for this run — re-run with `--keep` (or `GSD_QA_KEEP=1`) to get a reproducible command.';
return 'NOT RUNNABLE: the scenario tree was not preserved for this run — re-run with `--keep` (or `MSD_QA_KEEP=1`) to get a reproducible command.';
}
const gsdToolsPath = path.resolve(__dirname, '..', '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
const msdToolsPath = path.resolve(__dirname, '..', '..', 'msd-core', 'bin', 'msd-tools.cjs');
// The trailing `# ...` is a shell comment, not part of the command: pasting
// the whole line (including the note) into a shell still runs correctly,
// since everything from `#` to end-of-line is ignored. This keeps the
// string a single copy-pasteable line while telling a human reader the
// walk also pins a clock and clears ambient env vars this repro cannot.
return `cd ${preservedDir} && node ${gsdToolsPath} --json-errors ${argv.join(' ')}`
+ ' # note: the walk also pins GSD_NOW_MS and clears ambient GSD_* vars, so results may differ slightly';
return `cd ${preservedDir} && node ${msdToolsPath} --json-errors ${argv.join(' ')}`
+ ' # note: the walk also pins MSD_NOW_MS and clears ambient MSD_* vars, so results may differ slightly';
}
/**

View File

@@ -17,14 +17,14 @@
* oracle is how a QA tool reports green on a broken engine.
*
* Every KIND below corresponds to a shape the real CLI actually produces; each
* was observed against `gsd-tools`, not imagined. See
* `.gsd/phase/test-2966-loop-qa-walk/40-design.md` for the behavior table.
* was observed against `msd-tools`, not imagined. See
* `.msd/phase/test-2966-loop-qa-walk/40-design.md` for the behavior table.
*/
const fs = require('node:fs');
/**
* Frozen classification of a single `gsd-tools` invocation.
* Frozen classification of a single `msd-tools` invocation.
*
* Tests assert on these constants, never on their string values, so renaming a
* value is a one-line change here rather than a sweep through the suite.
@@ -74,9 +74,9 @@ function tryParseJson(text) {
/**
* Return the last non-empty line of a stream.
*
* Load-bearing: `gsd-tools` writes operator warnings to stderr *before* the
* Load-bearing: `msd-tools` writes operator warnings to stderr *before* the
* structured error envelope — e.g.
* `gsd-tools: warning: unknown config key(s) in .planning/config.json: …`
* `msd-tools: warning: unknown config key(s) in .planning/config.json: …`
* followed by `{"ok":false,…}`. Parsing the whole stderr blob therefore fails
* on a command that is in fact conforming. During research this exact shape
* produced a false positive against `review-lane`, which is why this function

View File

@@ -3,18 +3,18 @@
/**
* run-report.cjs — developer/CI entry point that discovers every QA-walk
* scenario, runs it for real against `gsd-tools`, and writes a single
* scenario, runs it for real against `msd-tools`, and writes a single
* `qa-report.json` document (see `report.cjs`).
*
* This is a TOOL, not a test file — it is invoked directly with `node`,
* never through `gsd-test` / `node --test`, and is deliberately NOT named
* never through `msd-test` / `node --test`, and is deliberately NOT named
* `*.test.cjs` so it is never picked up by the test runner's glob.
*
* Usage:
* node tests/qa/run-report.cjs [--out <path>] [--keep]
*
* `--out <path>` defaults to `qa-report.json` at the repo root.
* `--keep` (or `GSD_QA_KEEP=1`) preserves every scenario's temp project
* `--keep` (or `MSD_QA_KEEP=1`) preserves every scenario's temp project
* directory instead of deleting it, and threads real repro commands for it
* into the report — see `report.cjs`'s `buildRepro`.
*/
@@ -88,7 +88,7 @@ function discoverScenarioFiles() {
*
* @param {{keep?: boolean, liveCommands?: string[]}} [opts] `keep` (default
* `false`) is forwarded to `runScenario` — see its own header for the
* `GSD_QA_KEEP=1` interaction. `liveCommands` defaults to a fresh call to
* `MSD_QA_KEEP=1` interaction. `liveCommands` defaults to a fresh call to
* `getLiveCommandTokens()` when omitted.
* @returns {Array<ReturnType<typeof runScenario> & {fixture: string}>}
* @throws {Error} when no scenario files are discovered.

View File

@@ -6,7 +6,7 @@
* WHY THIS FILE EXISTS
* ────────────────────
* A scenario is a small JSON document describing a sequence of loop-host
* points, artifacts an "agent" would write at each point, and `gsd-tools`
* points, artifacts an "agent" would write at each point, and `msd-tools`
* invocations to run there. `loadScenario` validates the JSON shape so a
* malformed scenario fails fast and loud (never a silent no-op walk);
* `runScenario` drives a `LoopWalk` through the steps, evaluating both
@@ -19,7 +19,7 @@
const fs = require('node:fs');
const path = require('node:path');
const { resolveRef } = require('./fixtures/index.cjs');
const { LOOP_HOST_CONTRACT } = require('../../gsd-core/bin/lib/loop-host-contract.cjs');
const { LOOP_HOST_CONTRACT } = require('../../msd-core/bin/lib/loop-host-contract.cjs');
const { MUTATIONS, apply, NOOP } = require('./mutations.cjs');
const { resolveWithin, isAbsoluteLike, hasTraversalSegment } = require('./paths.cjs');
@@ -309,7 +309,7 @@ function evaluateExpectations(expectations, result) {
* keep?: boolean,
* }} opts `keep` (default `false`) preserves the walk's temp project instead
* of removing it in `finally` — see `LoopWalk#cleanup`. Also honored via
* the `GSD_QA_KEEP=1` environment variable (an `||`, not an override: either
* the `MSD_QA_KEEP=1` environment variable (an `||`, not an override: either
* one being truthy keeps the tree), since a CI operator invoking this
* through a shell cannot pass a JS option.
* @returns {{
@@ -328,7 +328,7 @@ function runScenario(scenario, opts) {
if (typeof runOracles !== 'function') {
throw new Error('runScenario: opts.runOracles (function) is required');
}
const shouldKeep = keep || process.env.GSD_QA_KEEP === '1';
const shouldKeep = keep || process.env.MSD_QA_KEEP === '1';
const walk = LoopWalk.create({ fixture: scenario.fixture });
/** @type {object[]} */
@@ -405,7 +405,7 @@ function runScenario(scenario, opts) {
const readOnly = !!step.readOnly;
const runs = Array.isArray(step.run) ? step.run : [];
// `step.jsonErrors === false` opts a step into the human-invocation
// path (`gsd_run <cmd>` without `--json-errors`); any other value
// path (`msd_run <cmd>` without `--json-errors`); any other value
// (including undefined) keeps `LoopWalk#run`'s own default of true.
const jsonErrorMode = step.jsonErrors !== false;
const runOptions = { jsonErrors: jsonErrorMode };

View File

@@ -8,24 +8,24 @@
* epic #4272).
*
* WHAT'S EXECUTABLE VS WHAT ISN'T (see
* `.gsd/phase/chore-4298-tdd-walk-qa-harness/40-design.md`): the two TDD
* `.msd/phase/chore-4298-tdd-walk-qa-harness/40-design.md`): the two TDD
* dispatch backends' `${TDD_APPLICABLE ? "..." : ""}` embed-list markers are
* LLM-orchestrator pseudo-syntax composed by the agent reading the workflow
* markdown at dispatch time — they are NOT real bash and cannot be executed
* by a shell. What IS real, executable bash is the resolution block that
* computes the `TDD_APPLICABLE` variable itself:
* - `gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md`
* - `msd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md`
* ("harness" backend) — one self-contained fenced block: its own
* `gsd_run` shim-discovery preamble, then the `TDD_APPLICABLE_RAW=...` /
* `msd_run` shim-discovery preamble, then the `TDD_APPLICABLE_RAW=...` /
* `TDD_APPLICABLE_RC=$?` / fail-closed check / `TDD_APPLICABLE=` assignment.
* - `gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md`
* ("worktree" backend) — the `gsd_run` shim-discovery preamble lives in
* - `msd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md`
* ("worktree" backend) — the `msd_run` shim-discovery preamble lives in
* the file's FIRST fenced block, which is not just the shim: it is the
* whole ISOLATION resolution (its own independent fail-closed branches),
* ahead of a LATER fenced block that contains the `_TDD_APPLICABLE_RAW=...`
* resolution followed by the `EXECUTOR_PROMPT=` composition this harness
* does not need. A standalone execution must concatenate the first block
* (for its `gsd_run` definition) with just the TDD resolution lines
* (for its `msd_run` definition) with just the TDD resolution lines
* sliced out of the second.
*
* This module extracts each backend's real bash via `indexOf`/`slice` on the
@@ -40,16 +40,16 @@
const fs = require('fs');
const path = require('path');
const { execFileSync } = require('child_process');
const { createTempProject, cleanup, runGsdTools, readFileNormalized, TEST_ENV_BASE } = require('../helpers.cjs');
const { createTempProject, cleanup, runMsdTools, readFileNormalized, TEST_ENV_BASE } = require('../helpers.cjs');
/** Absolute real repo root — NOT the temp fixture's own root. */
const REPO_ROOT = path.join(__dirname, '..', '..');
const RESOLUTION_STEP_PATH = path.join(
REPO_ROOT, 'gsd-core', 'workflows', 'execute-phase', 'steps', 'tdd-applicability-resolution.md',
REPO_ROOT, 'msd-core', 'workflows', 'execute-phase', 'steps', 'tdd-applicability-resolution.md',
);
const DISPATCH_STEP_PATH = path.join(
REPO_ROOT, 'gsd-core', 'workflows', 'execute-phase', 'steps', 'executor-isolation-dispatch.md',
REPO_ROOT, 'msd-core', 'workflows', 'execute-phase', 'steps', 'executor-isolation-dispatch.md',
);
const FENCE_OPEN = '```bash';
@@ -192,8 +192,8 @@ function parseResultSentinel(stdout) {
// Bounds `bash -c` execution (below) of a REAL fenced bash block extracted
// from a workflow markdown file's resolution/dispatch step — a bash-hosted
// script that may itself shell out further (e.g. via a `gsd_run` shim), not
// a bare `gsd-tools.cjs` CLI subcommand invocation. Numerically coincides
// script that may itself shell out further (e.g. via a `msd_run` shim), not
// a bare `msd-tools.cjs` CLI subcommand invocation. Numerically coincides
// with tests/helpers/timeouts.cjs's `BUILD_TIMEOUT_MS` (hooks-bundle build
// via scripts/build-hooks.js, an unrelated operation) and with this batch's
// other file-local `PLAN_PRE_HOOK_CLI_TIMEOUT_MS` (a different file, a
@@ -216,19 +216,19 @@ const TDD_BACKEND_SCRIPT_TIMEOUT_MS = 30000;
*/
function executeBackendScript(script, cwd) {
try {
// #4298 Standards+Spec review: `runGsdTools` (tests/helpers.cjs) scrubs
// #4298 Standards+Spec review: `runMsdTools` (tests/helpers.cjs) scrubs
// SESSION_IDENTITY_ENV_KEYS + config-location env vars before spawning,
// because an ambient developer/CI value (e.g. a real ~/.gsd/config.json
// location override) can silently change what a gsd-tools child resolves
// — documented there as #2665. This harness spawns `gsd_run` the exact
// because an ambient developer/CI value (e.g. a real ~/.msd/config.json
// location override) can silently change what a msd-tools child resolves
// — documented there as #2665. This harness spawns `msd_run` the exact
// same way (indirectly, via the extracted resolution script), so it needs
// the same hermeticity: spread the exported `TEST_ENV_BASE` (every
// scrub-listed key set to '') AFTER `...process.env` but BEFORE the two
// intentional overrides below, so ambient state is blanked and only
// RUNTIME_DIR/GSD_TEST_MODE are deliberately set.
// RUNTIME_DIR/MSD_TEST_MODE are deliberately set.
const stdout = execFileSync('bash', ['-c', script], {
cwd,
env: { ...process.env, ...TEST_ENV_BASE, RUNTIME_DIR: REPO_ROOT, GSD_TEST_MODE: '1' },
env: { ...process.env, ...TEST_ENV_BASE, RUNTIME_DIR: REPO_ROOT, MSD_TEST_MODE: '1' },
encoding: 'utf8',
timeout: TDD_BACKEND_SCRIPT_TIMEOUT_MS,
});
@@ -271,7 +271,7 @@ class TddWalk {
* @returns {TddWalk}
*/
static create(opts = {}) {
const { prefix = 'gsd-tdd-walk-' } = opts;
const { prefix = 'msd-tdd-walk-' } = opts;
const dir = createTempProject(prefix);
return new TddWalk(dir);
}
@@ -288,7 +288,7 @@ class TddWalk {
}
/**
* Resolve TDD-applicability via the real CLI verb (`gsd_run query
* Resolve TDD-applicability via the real CLI verb (`msd_run query
* phase.tdd-applicable`), reusing Phase 1's proven invocation
* (`tests/phase-tdd-applicable.test.cjs`).
*
@@ -300,7 +300,7 @@ class TddWalk {
const { cliFlag = false } = opts;
const argv = ['query', 'phase.tdd-applicable', this.planPath];
if (cliFlag) argv.push('--cli-flag');
const result = runGsdTools(argv, this.dir);
const result = runMsdTools(argv, this.dir);
if (!result.success) {
return { success: false, error: result.error, exitCode: result.exitCode ?? null };
}