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:
@@ -142,7 +142,7 @@ function isMilestoneShippedInRoadmap(content: string, version: string): boolean
|
||||
return false;
|
||||
}
|
||||
|
||||
// #2761 B1: matches a bracket MILESTONE heading's intro (`[GSD.02]`) at the
|
||||
// #2761 B1: matches a bracket MILESTONE heading's intro (`[MSD.02]`) at the
|
||||
// START of a heading's text, capturing the bracket id in group 1. Shared by
|
||||
// isBracketMilestoneBoundary below — the ONE recognizer for "is this heading
|
||||
// bracket-shaped", so computeSectionEnd and the preambleCutoff scan cannot
|
||||
@@ -152,13 +152,13 @@ const BRACKET_HEADING_INTRO_RE = new RegExp(`^\\[(${BRACKET_ID_SRC})\\]`, 'i');
|
||||
|
||||
// #2761 B2 (round-2 fix, Blocker 2): ADR-612 Decision 1's own discriminator
|
||||
// (docs/adr/612-bracket-phase-id-convention.md:56) — "a phase heading is a
|
||||
// bracket followed by a digit-then-colon (`[GSD.02] 05:`); a milestone
|
||||
// bracket followed by a digit-then-colon (`[MSD.02] 05:`); a milestone
|
||||
// heading is a bracket followed by a name" — is CONTENT, not heading level.
|
||||
// The prior `h.level <= 2` level cap broke on a level-3 bracket milestone
|
||||
// heading (`### [GSD.02] Foundation`): its own level-3 phase children
|
||||
// (`#### [GSD.02] 01: One`) never reached this check at all (excluded
|
||||
// heading (`### [MSD.02] Foundation`): its own level-3 phase children
|
||||
// (`#### [MSD.02] 01: One`) never reached this check at all (excluded
|
||||
// upstream by the `h.level > level` sibling-depth filter), but a level-3
|
||||
// SIBLING milestone heading (`### [GSD.03] Later`) was ALSO excluded by the
|
||||
// SIBLING milestone heading (`### [MSD.03] Later`) was ALSO excluded by the
|
||||
// level cap, so the section ran to EOF instead of stopping there — trek-e's
|
||||
// original #612 defect, reopened on any milestone heading below level 2.
|
||||
//
|
||||
@@ -168,7 +168,7 @@ const BRACKET_HEADING_INTRO_RE = new RegExp(`^\\[(${BRACKET_ID_SRC})\\]`, 'i');
|
||||
// digit + optional-tag + colon tail every phase-heading counter in this file
|
||||
// already spells (mirrors the `([\w][\w.-]*)(?:\s*\([^)\n]{0,200}\))?\s*:`
|
||||
// shape at :nnn below) — not a re-typed grammar. Covers the dotted sub-phase
|
||||
// heading form too (`[GSD.02] 05.03:`) via the same `[\w][\w.-]*` token,
|
||||
// heading form too (`[MSD.02] 05.03:`) via the same `[\w][\w.-]*` token,
|
||||
// which admits an embedded `.`.
|
||||
const BRACKET_PHASE_TAIL_RE = new RegExp(
|
||||
`^${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.ANY_BRACKET, 'bracket', false)}[\\w][\\w.-]*(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:`,
|
||||
@@ -183,7 +183,7 @@ const BRACKET_PHASE_TAIL_RE = new RegExp(
|
||||
* dotted sub-phase form), which must never terminate a milestone's own
|
||||
* section, or (b) a heading that names the SAME milestone already selected,
|
||||
* which is a CONTINUATION of the current milestone's own section (a
|
||||
* version-less split like `## [GSD.02] Foundation (Phase Details)`), not the
|
||||
* version-less split like `## [MSD.02] Foundation (Phase Details)`), not the
|
||||
* boundary to a DIFFERENT one?
|
||||
*
|
||||
* `level` is the CANDIDATE heading's own depth (`h.level`), capped at 3 — a
|
||||
@@ -204,8 +204,8 @@ function isBracketMilestoneBoundary(headingText: string, level: number, selected
|
||||
if (level > 3) return false;
|
||||
const introMatch = BRACKET_HEADING_INTRO_RE.exec(headingText);
|
||||
if (!introMatch) return false;
|
||||
// #2761 B2: a bracket PHASE heading (`[GSD.02] 05:`, or the dotted
|
||||
// sub-phase form `[GSD.02] 05.03:`) is never a milestone boundary,
|
||||
// #2761 B2: a bracket PHASE heading (`[MSD.02] 05:`, or the dotted
|
||||
// sub-phase form `[MSD.02] 05.03:`) is never a milestone boundary,
|
||||
// regardless of level.
|
||||
if (BRACKET_PHASE_TAIL_RE.test(headingText)) return false;
|
||||
// #2761 B1: fold-before-identity — this branch's own convention
|
||||
@@ -224,7 +224,7 @@ function isBracketMilestoneBoundary(headingText: string, level: number, selected
|
||||
*
|
||||
* Used ONLY at the preambleCutoff scan below, to distinguish a genuine prior
|
||||
* or later sibling milestone — whose subtree contains a real phase heading
|
||||
* carrying its bracket id (`## [GSD.01] Setup` / `### [GSD.01] 01: …`)
|
||||
* carrying its bracket id (`## [MSD.01] Setup` / `### [MSD.01] 01: …`)
|
||||
* — from an unrelated bracket-shaped PROSE heading sitting above the current
|
||||
* milestone's own content. Same-id-ness alone is insufficient: round-4 F1's
|
||||
* `[ADR.612] Heading convention` owns `[ADR.612] Examples`, but that child is
|
||||
@@ -450,7 +450,7 @@ const MILESTONE_HEADING_SIGNAL_PATTERN = /v\d+\.\d+|✅|📋|🚧|\bMilestone\b/
|
||||
* / `## Phase 3:`), so "strictly deeper" never matches for either sibling
|
||||
* and the predicate answers false, letting the whole-document count
|
||||
* conflate them exactly as #1761 did. (b) #3204 reintroduced — the
|
||||
* bundled greenfield template itself (`gsd-core/templates/roadmap.md:149-171`:
|
||||
* bundled greenfield template itself (`msd-core/templates/roadmap.md:149-171`:
|
||||
* `## Phases` -> `### 🚧 v1.1 — …` -> `#### Phase 5: …`) nests a Phase
|
||||
* heading arbitrarily deep under EVERY ancestor in the chain, so a
|
||||
* generic wrapper heading ("Phases") with no milestone meaning of its own
|
||||
@@ -601,13 +601,13 @@ function sliceMilestoneWindow(content: string, version: string): string | null {
|
||||
*/
|
||||
// #3641: the bracket-convention phase-ENTRY heading shape — ADR-612 Decision
|
||||
// 1's own discriminator: a phase heading is a bracket followed by a
|
||||
// DIGIT-then-colon (`### [GSD.04] 01: Name`); a bracket followed by a NAME
|
||||
// DIGIT-then-colon (`### [MSD.04] 01: Name`); a bracket followed by a NAME
|
||||
// is a milestone heading and must never count. Every fragment interpolates a
|
||||
// single-owner export from phase-id.cts — the heading intro
|
||||
// (PHASE_HEADING_PREFIX_SRC: a `[...]` bracket optionally followed by a
|
||||
// `Phase ` label, or a bare `Phase ` label), the digit-bearing token
|
||||
// (PHASE_NUMBER_TOKEN_SOURCE, which also covers the dotted sub-phase form
|
||||
// `[GSD.02] 05.03:`), and the optional pre-colon tag
|
||||
// `[MSD.02] 05.03:`), and the optional pre-colon tag
|
||||
// (OPTIONAL_PHASE_TAG_SOURCE) — never a re-typed grammar. Tested IN
|
||||
// ADDITION to the legacy pattern below, so bracket mode is a strict
|
||||
// superset: mid-migration legacy-labeled headings (`Phase AUTH-101:`-style
|
||||
@@ -615,7 +615,7 @@ function sliceMilestoneWindow(content: string, version: string): string | null {
|
||||
// earlier single-alternative form with a `[\\w]` token admitted
|
||||
// `[bracket] Word:` shapes — a colon-bearing MILESTONE heading inside the
|
||||
// window read as an entry (defeating V005 outright for that spelling) and a
|
||||
// decoy `### [GSD.04] Notes:` outside the window manufactured a false V005
|
||||
// decoy `### [MSD.04] Notes:` outside the window manufactured a false V005
|
||||
// while suppressing the correct V004. The digit anchor forecloses both.
|
||||
const BRACKET_PHASE_ENTRY_HEADING_RE = new RegExp(
|
||||
`^${PHASE_HEADING_PREFIX_SRC}${PHASE_NUMBER_TOKEN_SOURCE}${OPTIONAL_PHASE_TAG_SOURCE}\\s*:`,
|
||||
@@ -797,7 +797,7 @@ function scanMilestonePhaseIds(
|
||||
* defined as a MIRROR of the parser's terminator vocabulary, and on this
|
||||
* branch that vocabulary is convention-SELECTED: `computeBracketSectionEnd`
|
||||
* adds `isBracketMilestoneBoundary` as a terminator arm, so the ADR-canonical
|
||||
* `## [GSD.09] Hidden` — no version token, no status emoji, not the word
|
||||
* `## [MSD.09] Hidden` — no version token, no status emoji, not the word
|
||||
* "Milestone" — terminates the window on an opted-in bracket repo while
|
||||
* matching NONE of the signals above. Left unmirrored, the guard accepts
|
||||
* exactly the description that narrows the window on the one convention this
|
||||
@@ -826,7 +826,7 @@ function findMilestoneScopeHeadingLines(text: string, convention: string | null
|
||||
//
|
||||
// `selectedBracketId` is deliberately `null`, so the same-milestone
|
||||
// CONTINUATION exemption never fires and a value naming the ACTIVE
|
||||
// milestone (`## [GSD.02] Foundation (Phase Details)`) is flagged even
|
||||
// milestone (`## [MSD.02] Foundation (Phase Details)`) is flagged even
|
||||
// though the real parser would treat it as a continuation. That is the
|
||||
// third instance of this function's stated conservatism and rests on the
|
||||
// same argument as the other two: WHICH milestone is active is a property
|
||||
@@ -877,7 +877,7 @@ function classifyMilestoneWindow(input: {
|
||||
* @param ws - #2562: workstream name, so the companion STATE.md is read from
|
||||
* `.planning/workstreams/<ws>/` instead of the project root. Omitted (the
|
||||
* default) preserves the prior `planningDir(cwd)` resolution exactly,
|
||||
* including its `GSD_WORKSTREAM` env fallback.
|
||||
* including its `MSD_WORKSTREAM` env fallback.
|
||||
*
|
||||
* #3184: `extractCurrentMilestone`'s CRITICAL blast radius (200+ affected
|
||||
* symbols, 20 direct callers) means its signature and return type do not
|
||||
@@ -887,7 +887,7 @@ function classifyMilestoneWindow(input: {
|
||||
* @param phaseIdConvention - #3641: the RESOLVED `phase_id_convention`
|
||||
* config value, threaded to `hasPhaseEntries` so the scope axis's row-8
|
||||
* comparison recognizes bracket-convention phase entries
|
||||
* (`### [GSD.04] 01: Name`). Optional: absent, or any value other than
|
||||
* (`### [MSD.04] 01: Name`). Optional: absent, or any value other than
|
||||
* `'bracket'`, compiles the legacy entry grammar byte-identically — the
|
||||
* widening engages only for a project that resolved the convention
|
||||
* explicitly. `extractCurrentMilestone`'s wrapper deliberately does NOT
|
||||
@@ -1170,9 +1170,9 @@ function extractCurrentMilestoneScoped(content: string, cwd?: string, ws?: strin
|
||||
// SUBTREE in the preamble, not merely its own inert heading text.
|
||||
// That subtree can still contain a DIFFERENT-id bracket PHASE
|
||||
// heading, which DOES form a qualified key and CAN admit a foreign
|
||||
// directory (F7: `## [GSD.01] Setup` / `### [GSD.07] 01: Foreign` —
|
||||
// no same-id child, so `[GSD.01] Setup` is not a boundary, and
|
||||
// `GSD.07-01-foreign`'s directory is admitted into the CURRENT
|
||||
// directory (F7: `## [MSD.01] Setup` / `### [MSD.07] 01: Foreign` —
|
||||
// no same-id child, so `[MSD.01] Setup` is not a boundary, and
|
||||
// `MSD.07-01-foreign`'s directory is admitted into the CURRENT
|
||||
// milestone's filter, reading 3/2/67%). This is NOT a regression —
|
||||
// base, round-1 and HEAD all read 3/2/67% on F7 (base via its own
|
||||
// pass-all degrade) — and it remains the declared OVER-inclusive,
|
||||
@@ -1455,7 +1455,7 @@ function getRoadmapPhaseInternal(cwd: string, phaseNum: unknown): RoadmapPhaseRe
|
||||
// #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
|
||||
if (isSentinelPhaseId(normalizedPhase)) return null;
|
||||
// Resolved INSIDE the try for the same reason as getMilestoneInfo below: planningDir
|
||||
// throws a plain Error for an invalid GSD_WORKSTREAM/GSD_PROJECT segment, and resolving
|
||||
// throws a plain Error for an invalid MSD_WORKSTREAM/MSD_PROJECT segment, and resolving
|
||||
// it outside let that escape uncaught, crashing every caller for a malformed workstream
|
||||
// name. ADR-227 is explicit that throwing breaks pipeline continuity, and this read path
|
||||
// has no reason to be the exception -- it already degrades to null for every other
|
||||
@@ -1628,7 +1628,7 @@ function hasNameableContent(s: string): boolean {
|
||||
* keeps the generic re-derivation. Anchoring on the known literal is what
|
||||
* makes a hostile STATE `milestone:` value (regex metacharacters, single-
|
||||
* segment `vN`, a literal `$&`/`$1`) resolve correctly: the generic pattern
|
||||
* only recognizes the real GSD version grammar and stops early on anything
|
||||
* only recognizes the real MSD version grammar and stops early on anything
|
||||
* outside it, leaving hostile characters in the extracted "name".
|
||||
*/
|
||||
function extractMilestoneHeadingName(
|
||||
@@ -1706,7 +1706,7 @@ function scoped(value: MilestoneInfo | null, scope: Scope): { value: MilestoneIn
|
||||
function getMilestoneInfo(cwd?: string): { value: MilestoneInfo | null; scope: Scope } {
|
||||
// Declared here but RESOLVED INSIDE the try, so the catch can name the file without
|
||||
// moving planningDir() out of the protected region. planningDir throws a plain Error
|
||||
// for an invalid GSD_WORKSTREAM/GSD_PROJECT segment, and hoisting the call let that
|
||||
// for an invalid MSD_WORKSTREAM/MSD_PROJECT segment, and hoisting the call let that
|
||||
// escape uncaught — breaking the invariant #2245 relies on, that this function never
|
||||
// throws. When the path never resolved there is nothing to name, so the diagnostic is
|
||||
// skipped and the default is returned exactly as before.
|
||||
@@ -1890,9 +1890,9 @@ type MilestonePhaseFilter = ((dirName: string) => boolean) & {
|
||||
* never see spurious warnings.
|
||||
* @param ws - #2562: workstream name, so the ROADMAP/STATE pair is read from
|
||||
* `.planning/workstreams/<ws>/` instead of the project root. Required by any
|
||||
* caller that iterates workstreams (it cannot set `GSD_WORKSTREAM` per
|
||||
* caller that iterates workstreams (it cannot set `MSD_WORKSTREAM` per
|
||||
* iteration). Omitted (the default) preserves the prior `planningDir(cwd)`
|
||||
* resolution exactly, including its `GSD_WORKSTREAM` env fallback — every
|
||||
* resolution exactly, including its `MSD_WORKSTREAM` env fallback — every
|
||||
* pre-#2562 call site is unaffected.
|
||||
*/
|
||||
function getMilestonePhaseFilter(cwd: string, versionOverride?: string | null, phaseIdConvention?: string | null, ws?: string | null): MilestonePhaseFilter {
|
||||
@@ -1906,7 +1906,7 @@ function getMilestonePhaseFilter(cwd: string, versionOverride?: string | null, p
|
||||
//
|
||||
// Stated exactly, because the narrower claim is the true one: this keeps
|
||||
// QUALIFIED IDS out of that flag's input, not hyphens in general. A heading
|
||||
// whose TOKEN carries its own hyphen (`### [GSD.02] Phase 02-01:`) still flips
|
||||
// whose TOKEN carries its own hyphen (`### [MSD.02] Phase 02-01:`) still flips
|
||||
// it through milestonePhaseNums — as it also does at base, which matches that
|
||||
// spelling through the un-widened intro. See the token-hyphen guard below.
|
||||
const milestoneQualifiedIds = new Set<string>();
|
||||
@@ -1937,9 +1937,9 @@ function getMilestonePhaseFilter(cwd: string, versionOverride?: string | null, p
|
||||
const hasPhaseHeadings = /#{2,4}\s*(?:\[[^\]]{1,200}\]\s*)?Phase\s+[\w]/i.test(roadmapContent);
|
||||
if (!hasVersionedMilestonesGlobal && hasPhaseHeadings && phaseIdConvention === 'milestone-prefixed') {
|
||||
console.warn(
|
||||
'[gsd] Deprecated: free-form ROADMAP.md detected (no versioned milestone headings). ' +
|
||||
'[msd] Deprecated: free-form ROADMAP.md detected (no versioned milestone headings). ' +
|
||||
'The project has phase_id_convention set to "milestone-prefixed" in config.json but the ' +
|
||||
'ROADMAP does not use versioned milestone headings. Run `gsd-tools roadmap upgrade --convention milestone-prefixed` to migrate (dry-run by default).'
|
||||
'ROADMAP does not use versioned milestone headings. Run `msd-tools roadmap upgrade --convention milestone-prefixed` to migrate (dry-run by default).'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2063,15 +2063,15 @@ function getMilestonePhaseFilter(cwd: string, versionOverride?: string | null, p
|
||||
// heading side selected. Without it the heading scan's new bracket reach was
|
||||
// half a fix: `milestonePhaseNums` became non-empty, so the pass-all degrade
|
||||
// stopped firing, but no bracket directory could satisfy the three legacy
|
||||
// checks below (numericRe fails on `GSD.02-05-five`, the custom-id match
|
||||
// captures the project code `GSD`, and stripProjectCodePrefix does not strip
|
||||
// checks below (numericRe fails on `MSD.02-05-five`, the custom-id match
|
||||
// captures the project code `MSD`, and stripProjectCodePrefix does not strip
|
||||
// a dotted prefix) — so EVERY bracket directory was rejected and
|
||||
// completed_phases / total_plans / completed_plans / percent collapsed to 0
|
||||
// while `state sync` went on writing a percent off the unfiltered disk.
|
||||
//
|
||||
// Matching is delegated to phaseTokenMatches against the milestone-QUALIFIED
|
||||
// id, not the bare token: READING-B puts the milestone in the bracket, so
|
||||
// `GSD.01-01-old-one` and `GSD.02-01-one` share the token `01` and only the
|
||||
// `MSD.01-01-old-one` and `MSD.02-01-one` share the token `01` and only the
|
||||
// qualified key separates them. That is the scoping this filter exists to do.
|
||||
// ADDITIVE: on a miss we fall through to the three legacy checks, so a
|
||||
// bracket repo carrying legacy-shaped directories reads exactly as before.
|
||||
@@ -2087,7 +2087,7 @@ function getMilestonePhaseFilter(cwd: string, versionOverride?: string | null, p
|
||||
// `^([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)*)` swallowed the WHOLE hyphenated
|
||||
// directory name (A-tool-output-contract was captured as
|
||||
// "A-tool-output-contract", not "A"), so every letter-named phase directory
|
||||
// (Phase A:..Phase L: — GSD's own convention, ADR-612 first-class non-numeric
|
||||
// (Phase A:..Phase L: — MSD's own convention, ADR-612 first-class non-numeric
|
||||
// IDs) fell out of the milestone and counts were silently fabricated over
|
||||
// whatever numeric directory survived. A letter-leading directory belongs if
|
||||
// its lowercased name EQUALS a declared phase ID, or BEGINS with that ID
|
||||
|
||||
Reference in New Issue
Block a user