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.
4.9 KiB
ADR 415: Prevent stale-base reintroduction of retired runtime tokens
- Status: Accepted (2026-05-28)
- Date: 2026-05-28
- Tracking issue: #415 (incident #411; fix #412; culprit #406; rename #373/#379)
Context
The $MSD_SDK → msd_run rename (#373/#379)
PRs #373 and #379 renamed the runtime resolver from the unquoted $MSD_SDK shell variable to a single-line, space-safe msd_run launcher. The launcher is defined in msd-core/workflows/_runtime-launcher.snippet.sh, propagated to all workflow .md files by scripts/sync-runtime-launcher.cjs, and enforced by tests/runtime-launcher-parity.test.cjs (which forbids any $MSD_SDK token in workflow markdown).
The silent regression (#406)
During a multi-PR merge sweep, PR #406 (fix(#160)) — branched before #379 — re-introduced 5 $MSD_SDK occurrences into msd-core/workflows/next.md. Because it edited a different region of the file than #379, the merge produced no textual conflict and Git accepted it silently.
#406's own CI was green because its base predated the parity test, and nothing re-checked the merge result against current next. #406 also carried a stale companion assertion (tests/policy-160-route0-resume.test.cjs) that required $MSD_SDK to be present.
Discovery and fix
The regression surfaced only when all PRs co-resided on next and the parity gate went red. Fixed in #411 and #412.
Root cause
A green PR on a stale base can still regress the integration branch via a semantic change that has no textual conflict. Textual conflicts are loud; semantic regressions are silent. Only a test run against the merge result catches them — which never happened because the base was stale and up-to-date-with-base was not required before merge.
Decision
-
Require up-to-date base before merge. Enable
required_status_checks.strict = trueonnext(already applied). Every PR must be up to date with the base before merging, forcing CI — includingruntime-launcher-parityand the full suite — to run against the actual merge result, catching silent semantic regressions before they land. -
The canonical propagator is the single source of truth for the runtime launcher. Never hand-author or hand-edit the launcher token in workflow
.mdfiles. Changes to the launcher form go through_runtime-launcher.snippet.sh+scripts/sync-runtime-launcher.cjs, gated byruntime-launcher-parity.test.cjs. The retired$MSD_SDKtoken must never reappear. -
Companion tests must track the canonical form. A test asserting the presence of a resolver token must assert the current canonical token (
msd_run), never a retired one; update propagated files and token-pinning tests in the same change. -
Admin-override caveat (process).
enforce_adminsremainsfalseso maintainers keep--adminfor routine flow. But because this incident was caused by--adminbatch-merging stale-base PRs, maintainers must not--admin-bypass the up-to-date requirement for any PR touching workflow.mdfiles (or other parity-gated, propagated artifacts): rebase and re-run the gate first. When batch-merging, merge structural-rename/propagation PRs last, or re-run the parity gate on the integration branch after the batch.
Consequences
Positive
- Silent stale-base semantic regressions (not just
$MSD_SDK) are caught pre-merge for normal merges. - The canonical-propagator rule and parity test give a single testable source of truth and an unambiguous reviewer/agent rule.
- Decision 4 names the exact failure mode for admin-bypass merges.
Negative
strict = trueadds rebase/CI churn: PRs behindnextmust update before merging.- The guard is not absolute.
--admincan still bypassstrict(enforce_admins = false), so admin merges rely on the Decision-4 discipline rather than a hard block. - Making it absolute would require
enforce_admins = true, intentionally not adopted — it would block the maintainer's routine--adminflow.
Alternatives Considered
(a) Documentation and discipline only — rejected. Discipline alone proved insufficient under batch merges.
(b) enforce_admins = true — rejected. Too heavy for a solo-maintainer repo dependent on --admin. Decision 4 addresses the admin path instead.
(c) A bespoke "retired-token" CI check diffing sync-script output — rejected as redundant. runtime-launcher-parity already forbids the token; the real gap was running it against the merge result, which Decision 1 fixes generally.
References
- Incident: #411
- Fix: #412
- Culprit PR: #406 (
fix(#160)) - Rename PRs: #373, #379 (
msd_run) - Propagator:
scripts/sync-runtime-launcher.cjs - Parity test:
tests/runtime-launcher-parity.test.cjs - Launcher snippet:
msd-core/workflows/_runtime-launcher.snippet.sh - Tracking issue: #415