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:
@@ -13,7 +13,7 @@
|
||||
// either scope widens.
|
||||
|
||||
/**
|
||||
* #4465 — /gsd:undo commit selection must be milestone-bounded and HEAD-reachable.
|
||||
* #4465 — /msd:undo commit selection must be milestone-bounded and HEAD-reachable.
|
||||
*
|
||||
* The defect: `--phase` documented a primary path reading `.planning/.phase-manifest.json`,
|
||||
* a file nothing in the repository writes, so the documented fallback was the only real
|
||||
@@ -26,7 +26,7 @@
|
||||
*
|
||||
* Two halves. The first pins the SHAPE of undo.md's fences (substring assertions over
|
||||
* the deployed prose — each half's own read site carries the marker). The second
|
||||
* EXECUTES those fences against a real git fixture and the real `gsd-tools.cjs`, so
|
||||
* EXECUTES those fences against a real git fixture and the real `msd-tools.cjs`, so
|
||||
* a fence that matches the expected text but does not do the expected thing still fails.
|
||||
*/
|
||||
|
||||
@@ -34,9 +34,9 @@ const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { scanFencedBlocks } = require('../gsd-core/bin/lib/markdown-sectionizer.cjs');
|
||||
const { scanFencedBlocks } = require('../msd-core/bin/lib/markdown-sectionizer.cjs');
|
||||
|
||||
const UNDO_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'undo.md');
|
||||
const UNDO_PATH = path.join(__dirname, '..', 'msd-core', 'workflows', 'undo.md');
|
||||
|
||||
/** Return the raw text of every ```bash fenced block in `content`. */
|
||||
function extractBashBlocks(content) {
|
||||
@@ -87,12 +87,12 @@ describe('#4465: undo commit selection is bounded', () => {
|
||||
});
|
||||
|
||||
test('O: every PHASE_DIR-scoped git call runs from the project root find-phase answers against', () => {
|
||||
// find-phase prints a path relative to the PROJECT ROOT (gsd-tools resolves it before
|
||||
// find-phase prints a path relative to the PROJECT ROOT (msd-tools resolves it before
|
||||
// dispatch), while the workflow's shell stays wherever the user invoked it. A pathspec is
|
||||
// read relative to git's cwd, so from a subdirectory a bare `git log -- "${PHASE_DIR}"`
|
||||
// matches nothing: the anchor comes back empty and a legitimate undo is refused, and the
|
||||
// collision check comes back empty and never fires (review round 5, self-found).
|
||||
const roots = (bash.match(/PROJECT_ROOT=\$\(gsd_run query planning inspect --pick generated_from\.cwd --raw/g) || []).length;
|
||||
const roots = (bash.match(/PROJECT_ROOT=\$\(msd_run query planning inspect --pick generated_from\.cwd --raw/g) || []).length;
|
||||
assert.equal(roots, 2, 'both modes must take PROJECT_ROOT from the same owner as find-phase (#4465)');
|
||||
const code = bash.split('\n').filter((l) => !/^\s*#/.test(l));
|
||||
// And from nowhere else: a rooted call is only as good as the root it is handed, so a later
|
||||
@@ -117,7 +117,7 @@ describe('#4465: undo commit selection is bounded', () => {
|
||||
const code = extractBashBlocks(content)
|
||||
.map((b) => b.split('\n').filter((l) => !/^\s*#/.test(l)).join('\n'));
|
||||
// The COMMON git directory is the repository's identity: a linked worktree has its own per-worktree
|
||||
// git dir but shares the object database, and gsd-tools maps its planning to the MAIN worktree, so a
|
||||
// git dir but shares the object database, and msd-tools maps its planning to the MAIN worktree, so a
|
||||
// per-worktree comparison refuses a legitimate undo there (review round 5, third pass).
|
||||
const gates = code.filter((b) => /PHASE_DIR_FOREIGN=""/.test(b)
|
||||
&& /_gd_here=\$\(_d=\$\(git rev-parse --git-common-dir 2>\/dev\/null\)/.test(b)
|
||||
@@ -136,7 +136,7 @@ describe('#4465: undo commit selection is bounded', () => {
|
||||
// find-phase resolves through planningDir, which roots an active workstream at
|
||||
// .planning/workstreams/<ws>/ — a hardcoded .planning/phases/ would read the
|
||||
// root's same-numbered phase instead.
|
||||
const uses = (bash.match(/gsd_run query find-phase/g) || []).length;
|
||||
const uses = (bash.match(/msd_run query find-phase/g) || []).length;
|
||||
assert.equal(uses, 2, 'both modes must resolve PHASE_DIR via find-phase (#4465)');
|
||||
});
|
||||
|
||||
@@ -221,7 +221,7 @@ describe('#4465: undo commit selection is bounded', () => {
|
||||
|
||||
test('H: dependency_check reads the workstream-resolved planning root', () => {
|
||||
assert.ok(
|
||||
/PLANNING_DIR=\$\(gsd_run query planning inspect --pick generated_from\.planning_root/.test(bash),
|
||||
/PLANNING_DIR=\$\(msd_run query planning inspect --pick generated_from\.planning_root/.test(bash),
|
||||
'dependency_check must resolve the planning root rather than hardcoding .planning/ (#4465)',
|
||||
);
|
||||
assert.ok(
|
||||
@@ -311,7 +311,7 @@ describe('#4465: undo commit selection is bounded', () => {
|
||||
// EXECUTES them: the exact ```bash fences the runtime runs are sliced out of
|
||||
// undo.md by content anchor, glued behind the inputs the workflow would have
|
||||
// set, and run with `bash -c` inside a real git fixture against the real
|
||||
// `gsd-tools.cjs` — the same createTempGitProject + fence-execution shape
|
||||
// `msd-tools.cjs` — the same createTempGitProject + fence-execution shape
|
||||
// new-milestone-clear-phases.test.cjs (#2308) and
|
||||
// code-review-pipeline-regression.test.cjs (#2352) use. A substring match cannot
|
||||
// tell a live invocation from a dead one; a run can.
|
||||
@@ -326,7 +326,7 @@ const { createTempGitProject, cleanup, readFileNormalized } = require('./helpers
|
||||
const { createFixture, seedPhase, seedWorkstream } = require('./fixtures/index.cjs');
|
||||
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const GSD_TOOLS_BIN = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
const MSD_TOOLS_BIN = path.join(__dirname, '..', 'msd-core', 'bin', 'msd-tools.cjs');
|
||||
const SKIP_WIN32 = process.platform === 'win32'
|
||||
? 'POSIX bash fence execution over a git fixture (see #2352 precedent)'
|
||||
: false;
|
||||
@@ -357,7 +357,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
|
||||
// gather_commits, MODE=phase: resolve → anchor → select
|
||||
const phaseResolve = fenceWhere(bodies, 'phase resolve',
|
||||
(b) => b.includes('PHASE_DIR=$(gsd_run query find-phase "${TARGET_PHASE}"'));
|
||||
(b) => b.includes('PHASE_DIR=$(msd_run query find-phase "${TARGET_PHASE}"'));
|
||||
const phaseArchivedGuard = fenceWhere(bodies, 'phase archived guard',
|
||||
(b) => b.includes('PHASE_DIR_ARCHIVED=""') && !b.includes('PLAN_PHASE'));
|
||||
// Located loosely on purpose: shape test C pins the anchor's spelling, and a locator that
|
||||
@@ -373,25 +373,25 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
(b) => b.includes('grep -E "\\(${TARGET_PLAN}\\):"'));
|
||||
// dependency_check: planning-root resolution
|
||||
const planningRoot = fenceWhere(bodies, 'planning root',
|
||||
(b) => b.includes('PLANNING_DIR=$(gsd_run query planning inspect'));
|
||||
(b) => b.includes('PLANNING_DIR=$(msd_run query planning inspect'));
|
||||
|
||||
// The composed script replays the fences in order in ONE shell, seeded with
|
||||
// what the workflow sets (TARGET_*), plus the real gsd_run over the real
|
||||
// what the workflow sets (TARGET_*), plus the real msd_run over the real
|
||||
// binary. That is deliberately the workflow's own data flow — `PHASE_DIR`,
|
||||
// `PHASE_START`, `UNDO_RANGE` are carried from fence to fence by the runtime
|
||||
// that executes undo.md — and it is what these tests prove: the selection
|
||||
// logic, not the runtime's variable transport between blocks.
|
||||
const GSD_RUN = 'gsd_run() { node "$GSD_TOOLS_BIN" "$@"; }';
|
||||
const MSD_RUN = 'msd_run() { node "$MSD_TOOLS_BIN" "$@"; }';
|
||||
|
||||
// `runIn` is the directory the shell starts in, when it is not the fixture root: the user
|
||||
// can invoke /gsd:undo from anywhere inside the project.
|
||||
// can invoke /msd:undo from anywhere inside the project.
|
||||
function runFences(cwd, seed, fences, tail = '', extraEnv = {}, runIn = cwd) {
|
||||
const script = [seed, GSD_RUN, ...fences, tail].join('\n');
|
||||
const env = { ...process.env, GSD_TOOLS_BIN, HOME: cwd };
|
||||
const script = [seed, MSD_RUN, ...fences, tail].join('\n');
|
||||
const env = { ...process.env, MSD_TOOLS_BIN, HOME: cwd };
|
||||
// A developer's active workstream must not leak into the fixture; a test that wants
|
||||
// one names it through `extraEnv`, applied after the scrub.
|
||||
delete env.GSD_WORKSTREAM;
|
||||
delete env.GSD_PROJECT;
|
||||
delete env.MSD_WORKSTREAM;
|
||||
delete env.MSD_PROJECT;
|
||||
Object.assign(env, extraEnv);
|
||||
const r = runHookSeam('-c', [script], { interpreter: 'bash', cwd: runIn, env, timeoutMs: PROBE_TIMEOUT_MS });
|
||||
throwIfFailed(r, 'bash <undo.md fences>');
|
||||
@@ -413,7 +413,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// The reported repro (#4465): milestone 1 ships phase 03 and is archived;
|
||||
// milestone 2 reuses the number. A dead branch carries a matching scope too.
|
||||
function multiMilestoneFixture() {
|
||||
const cwd = createTempGitProject('gsd-4465-mm-');
|
||||
const cwd = createTempGitProject('msd-4465-mm-');
|
||||
const mainBranch = gitOrThrow(['rev-parse', '--abbrev-ref', 'HEAD'], { cwd }).trim();
|
||||
seedPhase(cwd, '03-auth', { '03-01-PLAN.md': '# auth\n' });
|
||||
commitFile(cwd, 'src/auth.js', 'auth\n', 'feat(03-01): implement auth endpoint');
|
||||
@@ -474,7 +474,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// writes; find-phase never searches it. The tests below pin what find-phase actually
|
||||
// returns for BOTH layouts, so a change to either reader shows up here.
|
||||
function twoArchivedMilestonesFixture() {
|
||||
const cwd = createTempGitProject('gsd-4465-arch-');
|
||||
const cwd = createTempGitProject('msd-4465-arch-');
|
||||
seedPhase(cwd, '03-auth', { '03-01-PLAN.md': '# auth\n' });
|
||||
gitOrThrow(['add', '-A'], { cwd });
|
||||
gitOrThrow(['commit', '-q', '-m', 'docs(03-01): v1.0 phase plan'], { cwd });
|
||||
@@ -557,7 +557,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// live directory -- so the collision check asks history whether this exact path was ever
|
||||
// vacated (round 4: it used to look for an archived twin by layout, and missed ws-*).
|
||||
function reusedSlugFixture() {
|
||||
const cwd = createTempGitProject('gsd-4465-slug-');
|
||||
const cwd = createTempGitProject('msd-4465-slug-');
|
||||
seedPhase(cwd, '03-auth', { '03-01-PLAN.md': '# v1\n' });
|
||||
gitOrThrow(['add', '-A'], { cwd });
|
||||
gitOrThrow(['commit', '-q', '-m', 'docs(03-01): v1 plan'], { cwd });
|
||||
@@ -629,7 +629,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// passes. What it catches is the opposite failure -- a check that refuses on a NAME it
|
||||
// finds under milestones/ rather than on this path's history, which is the layout-keyed
|
||||
// shape round 4 replaced. Round 2 pinned it for a stray FILE against the old glob.
|
||||
const cwd = createTempGitProject('gsd-4465-file-twin-');
|
||||
const cwd = createTempGitProject('msd-4465-file-twin-');
|
||||
t.after(() => cleanup(cwd));
|
||||
seedPhase(cwd, '06-live', { '06-01-PLAN.md': '# live\n' });
|
||||
fs.mkdirSync(path.join(cwd, '.planning', 'milestones', 'v1.0-phases'), { recursive: true });
|
||||
@@ -652,7 +652,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// A CONTROL, like the one above: it passes with the collision check deleted, and fails only
|
||||
// if the check starts refusing on a directory name. `vnondigit-phases` is not a milestone
|
||||
// either reader of the archive tree admits, and this live path was never vacated.
|
||||
const cwd = createTempGitProject('gsd-4465-malformed-');
|
||||
const cwd = createTempGitProject('msd-4465-malformed-');
|
||||
t.after(() => cleanup(cwd));
|
||||
seedPhase(cwd, '05-live', { '05-01-PLAN.md': '# live\n' });
|
||||
fs.mkdirSync(path.join(cwd, '.planning', 'milestones', 'vnondigit-phases', '05-live'), { recursive: true });
|
||||
@@ -690,7 +690,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// .planning/workstreams/milestones/phases/NN-x as archived and refuses a phase that
|
||||
// is live and revertible — a fail-closed bug, but a bug. Only the archive LAYOUTS
|
||||
// (`v<X.Y>-phases/<phase>` and `ws-<name>-<date>/phases/<phase>`) separate the two.
|
||||
const cwd = createTempGitProject('gsd-4465-wsname-');
|
||||
const cwd = createTempGitProject('msd-4465-wsname-');
|
||||
t.after(() => cleanup(cwd));
|
||||
const dir = path.join(cwd, '.planning', 'workstreams', 'milestones', 'phases', '03-live');
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
@@ -700,11 +700,11 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
commitFile(cwd, 'src/live.js', 'l\n', 'feat(03-01): workstream work');
|
||||
// Activate by env, the same route find-phase honours for an active workstream.
|
||||
const script = [
|
||||
'TARGET_PHASE=03', GSD_RUN, phaseResolve, phaseArchivedGuard, phaseAnchor, phaseSelect,
|
||||
'TARGET_PHASE=03', MSD_RUN, phaseResolve, phaseArchivedGuard, phaseAnchor, phaseSelect,
|
||||
'echo "ARCHIVED=[${PHASE_DIR_ARCHIVED}]"',
|
||||
].join('\n');
|
||||
const env = { ...process.env, GSD_TOOLS_BIN, HOME: cwd, GSD_WORKSTREAM: 'milestones' };
|
||||
delete env.GSD_PROJECT;
|
||||
const env = { ...process.env, MSD_TOOLS_BIN, HOME: cwd, MSD_WORKSTREAM: 'milestones' };
|
||||
delete env.MSD_PROJECT;
|
||||
const r = runHookSeam('-c', [script], { interpreter: 'bash', cwd, env, timeoutMs: PROBE_TIMEOUT_MS });
|
||||
throwIfFailed(r, 'bash <undo.md fences>');
|
||||
assert.ok(r.stdout.includes('ARCHIVED=[]'),
|
||||
@@ -720,7 +720,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// archived phase's former path is `.planning/workstreams/<name>/phases/<phase>`. Re-creating
|
||||
// the workstream under the same name with the same phase slug re-creates that exact path.
|
||||
function workstreamArchiveFixture({ recreate }) {
|
||||
const cwd = createTempGitProject('gsd-4465-ws-');
|
||||
const cwd = createTempGitProject('msd-4465-ws-');
|
||||
commitFile(cwd, '.planning/workstreams/feat/phases/03-auth/03-01-PLAN.md', '# gen 1\n',
|
||||
'docs(03-01): feat generation-1 plan');
|
||||
commitFile(cwd, 'src/gen1.js', 'g1\n', 'feat(03-01): feat generation-1 work');
|
||||
@@ -737,7 +737,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
}
|
||||
return cwd;
|
||||
}
|
||||
const WS_FEAT = { GSD_WORKSTREAM: 'feat' };
|
||||
const WS_FEAT = { MSD_WORKSTREAM: 'feat' };
|
||||
|
||||
test('negative control: WITHOUT the collision guard, a re-created workstream selects its archived generation too', (t) => {
|
||||
const cwd = workstreamArchiveFixture({ recreate: true });
|
||||
@@ -802,7 +802,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// would run forward from it -- the exact contamination the refusal exists for.
|
||||
const cwd = workstreamArchiveFixture({ recreate: false });
|
||||
t.after(() => cleanup(cwd));
|
||||
const stub = 'gsd_run() { echo ".planning/milestones/ws-feat-2026-09-01/phases/03-auth"; }';
|
||||
const stub = 'msd_run() { echo ".planning/milestones/ws-feat-2026-09-01/phases/03-auth"; }';
|
||||
const report = 'printf "ARCHIVED=[%s]\\nPHASE_DIR=[%s]\\nUNDO_RANGE=[%s]\\n" "$PHASE_DIR_ARCHIVED" "$PHASE_DIR" "$UNDO_RANGE"';
|
||||
for (const [seed, fences] of [
|
||||
['TARGET_PHASE=03', [stub, phaseResolve, phaseArchivedGuard, phaseAnchor, phaseSelect]],
|
||||
@@ -820,7 +820,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
test('self-found: a phase REMOVED and re-added under the same slug is refused too', (t) => {
|
||||
// No archive anywhere -- the path was simply deleted and re-created. A layout glob has no
|
||||
// entry to find; the history check sees the vacancy. Same anchor defect, same refusal.
|
||||
const cwd = createTempGitProject('gsd-4465-readd-');
|
||||
const cwd = createTempGitProject('msd-4465-readd-');
|
||||
t.after(() => cleanup(cwd));
|
||||
commitFile(cwd, '.planning/phases/03-auth/03-01-PLAN.md', '# first\n', 'docs(03-01): first plan');
|
||||
commitFile(cwd, 'src/first.js', 'f\n', 'feat(03-01): first attempt');
|
||||
@@ -835,7 +835,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
});
|
||||
|
||||
function droppedPlanFixture() {
|
||||
const cwd = createTempGitProject('gsd-4465-dropplan-');
|
||||
const cwd = createTempGitProject('msd-4465-dropplan-');
|
||||
commitFile(cwd, '.planning/phases/03-auth/03-01-PLAN.md', '# one\n', 'docs(03-01): plan one');
|
||||
commitFile(cwd, '.planning/phases/03-auth/03-02-PLAN.md', '# two\n', 'docs(03-02): plan two');
|
||||
gitOrThrow(['rm', '-q', '.planning/phases/03-auth/03-02-PLAN.md'], { cwd });
|
||||
@@ -863,7 +863,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
'the whole phase must still be selectable');
|
||||
});
|
||||
|
||||
// Review round 5, self-found: the user can run /gsd:undo from anywhere inside the project.
|
||||
// Review round 5, self-found: the user can run /msd:undo from anywhere inside the project.
|
||||
// find-phase answers relative to the PROJECT ROOT; a pathspec is read relative to git's cwd.
|
||||
// Every test above runs from the fixture root, where the two coincide and the mismatch is
|
||||
// invisible. These run the same fences from `sub/dir`.
|
||||
@@ -936,7 +936,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// the code in child repositories. From a child, findProjectRoot returns the parent, so the project
|
||||
// root and the repository the commits live in are different repositories.
|
||||
function subReposFixture(t) {
|
||||
const parent = createFixture({ prefix: 'gsd-4465-subrepos-', planning: false, git: true, projectDoc: false });
|
||||
const parent = createFixture({ prefix: 'msd-4465-subrepos-', planning: false, git: true, projectDoc: false });
|
||||
t.after(() => cleanup(parent));
|
||||
fs.writeFileSync(path.join(parent, '.gitignore'), 'child/\n');
|
||||
fs.mkdirSync(path.join(parent, '.planning'), { recursive: true });
|
||||
@@ -950,7 +950,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
commitFile(child, 'a.js', 'a\n', 'feat(03-01): old child work');
|
||||
commitFile(child, 'b.js', 'b\n', 'feat(03-01): current child work');
|
||||
// HOME off the parent, so the sub_repos branch of findProjectRoot is the one that resolves.
|
||||
const home = createFixture({ prefix: 'gsd-4465-home-', planning: false, git: false });
|
||||
const home = createFixture({ prefix: 'msd-4465-home-', planning: false, git: false });
|
||||
t.after(() => cleanup(home));
|
||||
return { parent, child, env: { HOME: home } };
|
||||
}
|
||||
@@ -997,14 +997,14 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
assert.deepEqual(subjects(out.replace(/(PHASE_START|UNDO_RANGE)=.*\n?/g, '')), []);
|
||||
});
|
||||
|
||||
// Review round 5, third pass: a LINKED worktree. gsd-tools maps a linked worktree with no
|
||||
// Review round 5, third pass: a LINKED worktree. msd-tools maps a linked worktree with no
|
||||
// .planning/ of its own to the MAIN worktree (resolveMainWorktreeCwd), so PROJECT_ROOT is the main
|
||||
// checkout while the shell sits in the linked one: two worktrees, one repository. The anchor then
|
||||
// comes from the main branch's history, which the linked HEAD may or may not contain.
|
||||
function linkedWorktreeFixture(t, { branchAfterPhase }) {
|
||||
const main = createTempGitProject('gsd-4465-wt-main-');
|
||||
const main = createTempGitProject('msd-4465-wt-main-');
|
||||
t.after(() => cleanup(main));
|
||||
const linked = createFixture({ prefix: 'gsd-4465-wt-linked-', planning: false, git: false });
|
||||
const linked = createFixture({ prefix: 'msd-4465-wt-linked-', planning: false, git: false });
|
||||
t.after(() => cleanup(linked));
|
||||
const phase = () => {
|
||||
seedPhase(main, '03-auth', { '03-01-PLAN.md': '# plan\n' });
|
||||
@@ -1015,7 +1015,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
if (branchAfterPhase) phase();
|
||||
gitOrThrow(['worktree', 'add', '-q', '-b', 'feature', linked], { cwd: main });
|
||||
if (!branchAfterPhase) phase();
|
||||
// No .planning/ in the linked checkout: that is what sends gsd-tools to the main worktree.
|
||||
// No .planning/ in the linked checkout: that is what sends msd-tools to the main worktree.
|
||||
gitOrThrow(['rm', '-rq', '.planning'], { cwd: linked });
|
||||
gitOrThrow(['commit', '-q', '-m', 'chore: executor worktree without planning'], { cwd: linked });
|
||||
commitFile(linked, 'src/b.js', 'b\n', 'feat(03-01): linked work');
|
||||
@@ -1065,7 +1065,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
// directory holding .planning/, so `git rev-parse --show-toplevel` would be the wrong -C
|
||||
// here; generated_from.cwd is the directory find-phase itself resolved against. Run from the
|
||||
// project root, so this pins the choice of root, not the subdirectory case above.
|
||||
const repo = createFixture({ prefix: 'gsd-4465-nested-', planning: false, git: true, projectDoc: false });
|
||||
const repo = createFixture({ prefix: 'msd-4465-nested-', planning: false, git: true, projectDoc: false });
|
||||
t.after(() => cleanup(repo));
|
||||
const project = path.join(repo, 'app');
|
||||
commitFile(repo, 'app/.planning/phases/03-auth/03-01-PLAN.md', '# nested\n', 'docs(03-01): nested plan');
|
||||
@@ -1081,7 +1081,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
});
|
||||
|
||||
test('single-milestone selection is unchanged: every phase commit, none from a later phase', (t) => {
|
||||
const cwd = createTempGitProject('gsd-4465-single-');
|
||||
const cwd = createTempGitProject('msd-4465-single-');
|
||||
t.after(() => cleanup(cwd));
|
||||
seedPhase(cwd, '03-auth', { '03-01-PLAN.md': '# auth\n' });
|
||||
commitFile(cwd, 'src/a.js', 'a\n', 'feat(03-01): implement auth endpoint');
|
||||
@@ -1100,7 +1100,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
});
|
||||
|
||||
test('limit-1: a matching commit one before PHASE_START is excluded; PHASE_START itself is included', (t) => {
|
||||
const cwd = createTempGitProject('gsd-4465-limit-');
|
||||
const cwd = createTempGitProject('msd-4465-limit-');
|
||||
t.after(() => cleanup(cwd));
|
||||
// Matching scope, committed BEFORE the phase directory exists: outside the window.
|
||||
commitFile(cwd, 'src/pre.js', 'pre\n', 'feat(03-01): stray pre-phase commit');
|
||||
@@ -1118,7 +1118,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
|
||||
test('root commit: a phase whose first commit is the repository root is fully selected', (t) => {
|
||||
// createTempGitProject seeds an initial commit, so build the root by hand.
|
||||
const cwd = createFixture({ prefix: 'gsd-4465-root-', planning: true, git: false });
|
||||
const cwd = createFixture({ prefix: 'msd-4465-root-', planning: true, git: false });
|
||||
t.after(() => cleanup(cwd));
|
||||
const g = (args) => gitOrThrow(args, { cwd });
|
||||
g(['init', '-q']);
|
||||
@@ -1157,7 +1157,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
});
|
||||
|
||||
test('workstream: --phase resolves the ACTIVE workstream\'s phase directory, not the root\'s', (t) => {
|
||||
const cwd = createTempGitProject('gsd-4465-ws-');
|
||||
const cwd = createTempGitProject('msd-4465-ws-');
|
||||
t.after(() => cleanup(cwd));
|
||||
// Root scope: phase 03 exists and has a matching commit.
|
||||
seedPhase(cwd, '03-root', { '03-01-PLAN.md': '# root\n' });
|
||||
@@ -1180,7 +1180,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
});
|
||||
|
||||
test('dependency_check: PLANNING_DIR resolves the active workstream root, and falls back to .planning without one', (t) => {
|
||||
const cwd = createTempGitProject('gsd-4465-pd-');
|
||||
const cwd = createTempGitProject('msd-4465-pd-');
|
||||
t.after(() => cleanup(cwd));
|
||||
const tail = 'echo "PLANNING_DIR=${PLANNING_DIR}"';
|
||||
const flat = runFences(cwd, '', [planningRoot], tail);
|
||||
@@ -1191,7 +1191,7 @@ describe('#4465: undo commit selection — executed against a git fixture', { sk
|
||||
`expected the active workstream's root; got:\n${ws}`);
|
||||
// And the fallback is real, not the only path: with no .planning at all the
|
||||
// pick yields '' (planning_root is null) and the fence lands on the literal.
|
||||
const bare = createFixture({ prefix: 'gsd-4465-noplan-', planning: false, git: true, projectDoc: false });
|
||||
const bare = createFixture({ prefix: 'msd-4465-noplan-', planning: false, git: true, projectDoc: false });
|
||||
t.after(() => cleanup(bare));
|
||||
const none = runFences(bare, '', [planningRoot], tail);
|
||||
assert.ok(none.includes('PLANNING_DIR=.planning'), `expected the literal fallback; got:\n${none}`);
|
||||
|
||||
Reference in New Issue
Block a user