Skip to content

ci(release): enforce canonical SemVer release-candidate identities #858

Description

@DecisionNerd

Problem

GraphForge publishes one coordinated release across SemVer-native ecosystems and Python. Without one canonical prerelease identity, spellings such as 0.6.0-rc.1, 0.6.0-rc1, and 0.6.0rc1 can drift across manifests, tags, registries, provenance, checksums, and recovery evidence. Python's normalized spelling is necessary at its boundary but must not become a second release identity.

Objective

Define and enforce one structured prerelease version contract for v0.6.0 and later coordinated releases:

  • canonical SemVer: MAJOR.MINOR.PATCH-rc.N
  • Git tag/GitHub prerelease: vMAJOR.MINOR.PATCH-rc.N
  • PyPI projection only: MAJOR.MINOR.PATCHrcN

Requirements

  • Parse versions structurally with SemVer/PEP 440-aware tooling; do not validate by substring matching.
  • Require lowercase rc, a dot-separated numeric candidate identifier, and no leading zero in N.
  • Use the Git-only v prefix only for tags and GitHub release identities.
  • Keep Cargo/crates.io, npm native/main/CLI/skills, manifests, artifact names, provenance, checksums, and evidence on canonical SemVer.
  • Normalize to PEP 440 only at the Python package boundary and verify the reverse mapping to the same canonical release.
  • Reject 0.6.0-rc1, 0.6.0rc1, 0.6.0-RC.1, 0.6.0-rc.01, mismatched RC counters, final/prerelease mixtures, and registry-specific drift.
  • Preserve immutable RC semantics: a failed candidate advances to the next rc.N; published versions and tags are never reused or overwritten.
  • Apply the contract to release preparation, Binding RC, publication, recovery, registry observation, and closeout evidence.

Acceptance Criteria

  • A single authoritative version parser/model produces the canonical version, Git tag, and PyPI projection.
  • Release and policy gates fail before publication when any coordinated surface differs.
  • Positive fixtures cover 0.6.0-rc.1, multi-digit rc.10, and final 0.6.0.
  • Negative fixtures cover malformed, noncanonical, mismatched, reused, and mixed final/prerelease identities.
  • Release documentation and examples consistently use v0.6.0-rc.1 / 0.6.0-rc.1, with 0.6.0rc1 identified only as the PyPI projection.
  • Existing stable-version publication remains valid.

BDD Completion Scenarios

Scenario: Prepare the first coordinated candidate
Given the root release version is 0.6.0-rc.1
When release validation inspects every package, artifact, tag, and evidence surface
Then all SemVer-native surfaces use 0.6.0-rc.1
And the Git identity is v0.6.0-rc.1
And the Python identity is the verified projection 0.6.0rc1.

Scenario: Reject a noncanonical spelling
Given one npm or Cargo surface declares 0.6.0-rc1
When the release gate runs
Then it fails before publication with a stable field-specific diagnostic
And no tag or registry mutation occurs.

Scenario: Advance after a failed candidate
Given v0.6.0-rc.1 was published and cannot be reused
When another candidate is prepared
Then every coordinated surface advances to rc.2
And final v0.6.0 remains unconsumed.

Scenario: Promote the verified final release
Given an immutable candidate passed all release gates
When final 0.6.0 is prepared
Then no prerelease marker remains on any surface
And the final identity is validated independently of the prior candidate.

Implementation Notes

  • Extend the existing unified-release/version-alignment authority rather than introducing a second validator.
  • Keep Rust/repository release policy authoritative; host-language scripts may project but must not redefine the version.
  • Include the canonical version and every ecosystem projection in retained release evidence.

Observability

  • Emit sanitized, field-specific version mismatch diagnostics identifying the surface and expected canonical projection.
  • Retain exact canonical/projected versions in SHA-bound release evidence.

Security And Privacy

  • Version validation must occur before registry credentials or publication mutations are used.
  • Diagnostics must not expose registry tokens, environment secrets, or private URLs.

Testing

  • Unit tests for structured parsing, canonical formatting, ordering, and projections.
  • Policy mutation tests for each invalid spelling and cross-surface mismatch.
  • Release workflow fixture tests proving no-publication behavior on validation failure.
  • Regression tests for stable releases and multi-digit RC ordering.

Documentation

  • Update the unified release contract, release runbook, contributor release guidance, and v0.6.0 examples.
  • Explain that PyPI normalization is a projection, not the canonical cross-ecosystem identity.

Non-Goals

  • Changing SemVer or PEP 440 rules.
  • Supporting multiple canonical spellings.
  • Repurposing build metadata as an RC counter.
  • Publishing an RC as the default stable npm/PyPI channel.

Related Issues

  • Milestone M10: Coordinated GraphForge v0.6.0 Release

Open Questions

None.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ci-cdCI/CD configuration changestoolingDeveloper tooling and automation

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions