Normalize zero-padded release tags before the reusable workflows verify the installed version

Merged
UN
12d ago
+218-12

Problem

Release tags are date-shaped and zero-padded (v2026.11.02), but the daily release plan publishes npm versions without the padding (2026.11.2).

pm-github-sync.yml and pm-github-closeout.yml take PM_GITHUB_VERSION from the tag on the workflow commit. After pm package install, they compare it with the installed package.json version using a strict !==. Every caller pinned to a padded tag would fail that check:

  • v2026.11.01 and v2026.11.02 fail today;
  • every future tag for days 1 to 9 of a month would fail;
  • so would every tag for a month before October.

No fleet caller pins a padded tag yet. CodeRabbit found the bug on unbraind/pm-web#294, which pins v2026.11.02. Next week's Dependabot pin bumps would hit it across the fleet.

Change

  • Both workflows: a pure-bash normalize_pm_github_version removes the zero padding from each numeric component and keeps an optional -N suffix. It normalizes:

    • the tag candidate, before the ambiguity check;
    • the deprecated pm-github-version input, after validation.

    The arithmetic only ever runs on operands already validated as N.N.N or N.N.N-N. The strict validation and every existing refusal stay unchanged.

  • One shared rule: scripts/daily-release-plan.ts exports normalizeReleaseVersion, and both npm_version and the released-version comparison go through it. A table test checks that the workflow's result equals decideDailyRelease().npm_version for 2026.11.02, 2026.09.01, 2026.10.19 and 2027.01.01-2.

  • New rows in test/self-versioned-workflows.test.ts, run against both workflows:

    • a padded tag;
    • a padded tag with a suffix;
    • a padded legacy input;
    • a padded tag matching a normalized input, with no warning;
    • two tags on one commit that normalize to the same version, which is not ambiguous.

Evidence

  • node --test test/self-versioned-workflows.test.ts test/daily-release-plan.test.ts: 78/78 pass. Typecheck and lint pass.
  • Revert proof (orchestrator re-run): with the candidate normalization removed from pm-github-sync.yml, 7 rows fail; with it restored, everything passes.
  • test/closeout-workflow.test.ts fails locally exactly as it does on pristine main, while CI on that main commit is green. It is tracked as a host-environment follow-up.
  • The full coverage gate runs in CI only, because the host is shared and the gate needs a 12 GiB heap.

Items

Summary by Sourcery

Normalize zero-padded release versions across release planning and reusable workflow verification.

Bug Fixes:

  • Prevent reusable workflows from failing installed-version verification when release tags contain zero-padded date components.
  • Keep optional release suffixes and legacy version inputs consistent with normalized tag versions.

Enhancements:

  • Share release-version normalization between the daily release plan and reusable workflows to ensure published npm versions and workflow resolution agree.

Documentation:

  • Document the zero-padded release-tag verification fix in the unreleased changelog.

Tests:

  • Expand release-plan and reusable-workflow coverage for padded versions, suffixes, legacy inputs, large numeric components, and equivalent tags.

Chores:

  • Record the associated project issue and follow-up tracking metadata.

Summary by cubic

Fixes reusable workflow version resolution so release tags with zero padding like v2026.11.02 no longer fail install verification against the published npm version.

  • Both pm-github-sync.yml and pm-github-closeout.yml strip zero padding from tag candidates and the deprecated pm-github-version input, preserving -N suffixes.
  • scripts/daily-release-plan.ts now exports normalizeReleaseVersion and routes npm_version through it, so the workflow and release plan share one tag-to-version rule.
  • Normalization strips zeros as text rather than arithmetic, so components past 64 bits or beyond Number() precision can't wrap or round; an all-zero component stays 0.
  • New tests cover padded tags, suffixes, legacy inputs, a 20-digit component, an all-zero component, and multiple tags on one commit that normalize to the same version.
  • The closeout workflow suite fails on the local shared host but is green on CI; tracked separately as a follow-up.