feat: install and update msd from git release tags, not npm

MSD is not published to npm, so /msd-update and the SessionStart update
check could never find a release. Releases are now vX.Y.Z git tags plus a
stable branch:

- check-latest-version reads release tags via git ls-remote against the
  baked repository URL (latest = highest stable tag, next = incl. -rc)
- /msd-update reruns the bootstrap installer pinned to the checked tag and
  reads the changelog from that tag
- bootstrap.sh defaults to the stable branch and supports --local/--global
- package identity: changelog URL on stable, manual install via
  msd.golem15.com
- README quickstart and update how-to describe the one-line installer
- track the repo-root CLAUDE.md

Emitted-Drift-Ack-Growth: update.md — the update flow now explains the git-tag release lookup and pins the bootstrap installer to the checked tag, replacing the npm/npx wording
This commit is contained in:
Jakub Zych
2026-10-09 02:47:50 +02:00
parent bb581803ad
commit 6524974d93
18 changed files with 349 additions and 202 deletions

View File

@@ -67,7 +67,7 @@ describe('#4153 regression: unresolved update targets stop before later workflow
assert.match(step, /INSTALL_SCOPE` is `UNKNOWN`, `TARGET_RUNTIME` is empty, or `MSD_DIR` is empty/);
assert.match(step, /rerun from a valid installed runtime/i);
assert.match(step, /Rerun from a valid installed runtime: `\/msd:update`\./);
assert.match(step, /npx -y --package=@golem15\/msd-core@latest -- msd-core --global/);
assert.match(step, /curl -fsSL https:\/\/msd\.golem15\.com \| bash -s -- --global/);
assert.match(step, /target runtime \(`claude`, `opencode`, `codex`, `antigravity`\)/);
assert.ok(exit > unresolved, 'unresolved target must exit before the next step');
@@ -88,7 +88,7 @@ describe('#4153 regression: unresolved update targets stop before later workflow
const mutationSpies = [
{ name: 'version check', text: src, needle: 'check-latest-version.cjs', after: end },
{ name: 'custom-file detection', text: src, needle: 'detect-custom-files --config-dir', after: end },
{ name: 'resolved installer', text: src, needle: 'npx -y --package=@golem15/msd-core@"$TAG" -- msd-core "$RUNTIME_FLAG"', after: end },
{ name: 'resolved installer', text: src, needle: 'bash -s -- --ref "v$LATEST_VERSION" "$RUNTIME_FLAG"', after: end },
{ name: 'update-cache removal', text: src, needle: 'rm -f "$HOME/.cache/msd/msd-update-check"', after: end },
{ name: 'restore apply', text: src, needle: 'restore-custom-files --config-dir "$MSD_DIR" --apply', after: end },
{ name: 'patch check', text: src, needle: 'check_local_patches', after: end },
@@ -194,17 +194,16 @@ test('issue #815: version check threads the tag through check-latest-version.cjs
assert.match(WF, /check-latest-version\.cjs"? --json --tag "\$TAG"/);
});
test('issue #815: install uses the selected tag, not a hardcoded @latest', () => {
const robust = WF.match(/npx -y --package=@golem15\/msd-core@"\$TAG" -- msd-core/g) || [];
test('issue #815: install pins the release the channel resolved, not a moving branch', () => {
const pinned = WF.match(/bash -s -- --ref "v\$LATEST_VERSION"/g) || [];
const runUpdateStart = WF.indexOf('<step name="run_update">');
const runUpdateEnd = WF.indexOf('</step>', runUpdateStart);
assert.ok(runUpdateStart >= 0 && runUpdateEnd > runUpdateStart, 'run_update step must exist');
const runUpdate = WF.slice(runUpdateStart, runUpdateEnd);
assert.ok(robust.length >= 2, `expected >=2 tag-parameterized npx invocations, found ${robust.length}`);
assert.doesNotMatch(runUpdate, /--package=@golem15\/msd-core@latest -- msd-core/,
'install lines must not hardcode @latest once --next exists');
assert.doesNotMatch(runUpdate, /--package=@golem15\/msd-core@(?:latest|next|beta|canary|rc) -- msd-core/,
'install lines must use the $TAG variable, never a hardcoded dist-tag literal');
assert.ok(pinned.length >= 2, `expected >=2 version-pinned installer invocations, found ${pinned.length}`);
assert.doesNotMatch(runUpdate, /--ref (?:stable|next|main)\b/,
'install lines must pin the checked $LATEST_VERSION tag, never a moving branch');
assert.doesNotMatch(runUpdate, /\bnpx\b/, 'MSD is not published to npm; install must not use npx');
});
test('issue #815: command documents --next/--rc and routes it to the update workflow', () => {
@@ -315,15 +314,15 @@ __t3130('bug #3130: update.md contains no bare npx invocations (cache-stale form
);
});
__t3130('bug #3130: update.md has exactly two robust resolved-install invocations', () => {
__t3130('bug #3130: update.md has exactly two resolved-install invocations (local + global)', () => {
const start = src3130.indexOf('<step name="run_update">');
const end = src3130.indexOf('</step>', start);
assert3130.ok(start >= 0 && end > start, 'run_update step must exist');
const robust = (src3130.slice(start, end).match(/npx -y --package=@golem15\/msd-core@\S+ -- msd-core/g) || []);
const robust = (src3130.slice(start, end).match(/curl -fsSL https:\/\/msd\.golem15\.com \| bash -s -- --ref "v\$LATEST_VERSION" "\$RUNTIME_FLAG" --(?:local|global)/g) || []);
assert3130.strictEqual(
robust.length,
2,
`Expected two resolved-install npx invocations in update.md, found ${robust.length}`,
`Expected two resolved-install bootstrap invocations (local + global) in update.md, found ${robust.length}`,
);
});
});