feat(skills): add /release-recovery skill for npm/repo release desync - #36565
Open
Martin Hochel (Hotell) wants to merge 3 commits into
Open
feat(skills): add /release-recovery skill for npm/repo release desync#36565Martin Hochel (Hotell) wants to merge 3 commits into
Martin Hochel (Hotell) wants to merge 3 commits into
Conversation
This was referenced Aug 12, 2026
📊 Bundle size report✅ No changes found |
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new release-health diagnostic and recovery guidance to address the “npm publish succeeded but git push failed” desync scenario by deriving truth from the npm registry and guiding a guarded recovery workflow.
Changes:
- Introduces
scripts/executors/src/check-release-sync.tsto compare public package versions between a git ref (or working tree) and npm, with optional remote tag presence reporting and--jsonoutput. - Adds the
release-recoveryskill documentation/guardrails and wires it into both.agentsand.claudeskill layouts. - Updates
AGENTS.mdto list the new/release-recoveryskill.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| scripts/executors/src/check-release-sync.ts | New CLI tool to detect repo/npm version drift (and optionally missing tags) for all public packages. |
| AGENTS.md | Documents the new release-recovery skill in the skills table. |
| .claude/skills/release-recovery/SKILL.md | Adds the .claude indirection to the shared .agents skill definition. |
| .agents/skills/release-recovery/SKILL.md | Defines the release-recovery skill workflow and guardrails for diagnosis + recovery via PR. |
Suppressed comments (2)
scripts/executors/src/check-release-sync.ts:70
- Network/timeout errors in
fetchLatestVersioncurrently resolveundefined, which then gets reported asunpublished. This should be treated as anerrorso the output doesn't silently misclassify packages when the registry is unreachable.
request.on('timeout', () => {
request.destroy();
resolve(undefined);
});
request.on('error', () => resolve(undefined));
scripts/executors/src/check-release-sync.ts:178
- If
fetchLatestVersionfails (timeout/network/non-200),npmVersionis currently indistinguishable from a real 404 and gets classified asunpublished. Handling fetch failures explicitly here (and marking the packageerror) prevents false negatives in the diagnosis.
const statuses = await mapWithConcurrency(publicPackages, options.concurrency, async pkg => {
const npmVersion = await fetchLatestVersion(pkg.name);
const result: PackageStatus = {
name: pkg.name,
localVersion: pkg.localVersion,
npmVersion,
status: 'in-sync',
};
if (!npmVersion) {
result.status = 'unpublished';
return result;
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
Pull request demo site: URL |
When a release pipeline publishes to npm and then fails to push (expired or policy-blocked git PAT), npm and the repo are left out of sync: the versions and changelogs exist on the registry but not in git. Recovering this by hand is slow and error prone, and the June 30 v8 release needed exactly that. Adds a skill that diagnoses and repairs the desync, optionally pointed at the failed pipeline run. It derives the expected state from the npm registry rather than from a pipeline artifact, so it also works on releases that predate this tooling. Also adds check-release-sync, the read-only diagnostic behind it, which compares every public package's version in a git ref against the registry and classifies each as in-sync / npm-ahead / repo-ahead / unpublished. It is useful standalone for auditing release health. Deliberately does not recreate git tags: a recovery commit is not the commit the release was built from, so the tags would point at the wrong revision, and a v9 release spans ~90 packages. Missing tags can be reported with --check-tags but are treated as informational. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Every failure mode - non-200, timeout, socket error, malformed body - resolved to undefined, which was then classified as 'unpublished'. Unpublished packages are excluded from the recovery set, so a transient 429 or 5xx could hide a genuine desync in the exact situation this tool exists for. With one request per public package, brushing up against rate limiting is realistic. Registry lookups now return a discriminated result, so 'not published' (a real 404) stays distinguishable from 'could not reach the registry'. Failed lookups are classified as errors, transient failures are retried with backoff, and the 'in sync' all-clear is suppressed when any package could not be checked. Also clamps the worker count to at least 1: --concurrency 0 previously spawned no workers and resolved instantly with an array of holes, which looked like a clean run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Martin Hochel (Hotell)
force-pushed
the
feat/release-recovery-skill
branch
from
August 13, 2026 09:43
2857ec3 to
6f98634
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Previous Behavior
When a release pipeline publishes to npm and then fails to push (expired or policy-blocked git PAT), npm and the repo are left out of sync: versions and changelogs exist on the registry but not in git.
Recovering that is a manual, error-prone slog — reconstruct which packages were published, re-derive every bump, replay changelogs, and get it right under time pressure. The v8 release on 2026-06-30 needed exactly that: 18 packages published, every push retry
403, repo fixed by hand days later.There was no tooling to detect the desync either. It is currently possible for a package to sit out of sync indefinitely without anyone noticing.
New Behavior
Adds a
/release-recoveryskill that diagnoses and repairs the desync, optionally pointed at the failed pipeline run.It derives the expected state from the npm registry rather than from a pipeline artifact, so it also works on releases that predate this tooling — including desyncs nobody noticed at the time.
Also adds
check-release-sync, the read-only diagnostic behind it, which compares every public package's version in a git ref against the registry and classifies each asin-sync/npm-ahead/repo-ahead/unpublished. Useful standalone for auditing release health:Running it against
mastertoday surfaces a real, currently-outstanding desync:@fluentui/web-componentsis3.0.2in the repo but3.0.3on npm.Deliberately does not recreate git tags
A recovery commit is not the commit the release was built from, so tags created during recovery would point at the wrong revision — worse than missing tags. A v9 release also spans ~90 packages. Missing tags can be reported with
--check-tags, but are treated as informational.Notes for reviewers
beachball bumpwrites files but does not commit, tag or push — tagging/pushing live only inbumpAndPush, reachable only frompublish. Verified empirically: a full bump run changed 123 files and created 0 tags, 0 commits, 0 branches.bumpconsumes more change files than it bumps packages;type: "none"files are deleted without a bump. Normal, and matches a real release.scripts/executorsis private, so no change file is required.Independent of the other two PRs in this series (#36563, #36564) — mergeable in any order.
Related Issue(s)
N/A — follow-up to the 2026-06-30 v8 release incident.