Skip to content

feat(skills): add /release-recovery skill for npm/repo release desync - #36565

Open
Martin Hochel (Hotell) wants to merge 3 commits into
microsoft:masterfrom
Hotell:feat/release-recovery-skill
Open

feat(skills): add /release-recovery skill for npm/repo release desync#36565
Martin Hochel (Hotell) wants to merge 3 commits into
microsoft:masterfrom
Hotell:feat/release-recovery-skill

Conversation

@Hotell

Copy link
Copy Markdown
Contributor

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-recovery 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 — 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 as in-sync / npm-ahead / repo-ahead / unpublished. Useful standalone for auditing release health:

node -r ./scripts/ts-node/src/register ./scripts/executors/src/check-release-sync.ts \
  --remote origin --ref origin/master

Running it against master today surfaces a real, currently-outstanding desync: @fluentui/web-components is 3.0.2 in the repo but 3.0.3 on 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

  • The skill is diagnose-first and approval-gated: it reports a plan and waits before writing anything.
  • beachball bump writes files but does not commit, tag or push — tagging/pushing live only in bumpAndPush, reachable only from publish. Verified empirically: a full bump run changed 123 files and created 0 tags, 0 commits, 0 branches.
  • bump consumes more change files than it bumps packages; type: "none" files are deleted without a bump. Normal, and matches a real release.
  • scripts/executors is 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.

@github-actions

Copy link
Copy Markdown

📊 Bundle size report

✅ No changes found

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ts to compare public package versions between a git ref (or working tree) and npm, with optional remote tag presence reporting and --json output.
  • Adds the release-recovery skill documentation/guardrails and wires it into both .agents and .claude skill layouts.
  • Updates AGENTS.md to list the new /release-recovery skill.

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 fetchLatestVersion currently resolve undefined, which then gets reported as unpublished. This should be treated as an error so 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 fetchLatestVersion fails (timeout/network/non-200), npmVersion is currently indistinguishable from a real 404 and gets classified as unpublished. Handling fetch failures explicitly here (and marking the package error) 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.

Comment thread scripts/executors/src/check-release-sync.ts
Comment thread scripts/executors/src/check-release-sync.ts Outdated
@github-actions

Copy link
Copy Markdown

Pull request demo site: URL

Martin Hochel (Hotell) and others added 2 commits August 13, 2026 11:43
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>
@Hotell
Martin Hochel (Hotell) force-pushed the feat/release-recovery-skill branch from 2857ec3 to 6f98634 Compare August 13, 2026 09:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants