# Derive the SemVer release tag from pyproject.toml automatically ## Background `pyproject.toml`'s `version` field must stay in canonical PEP 440 form (`0.2.0rc15`, no hyphen) because that's the literal string hatchling embeds in the built sdist's filename — `publish_muse_release.sh` and `smoke_muse.sh` both derive the tarball URL directly from it. A hyphenated form (`0.2.0-rc15`) was tried during the rc15 release and broke the publish script's tarball-filename lookup (hatchling normalizes it away regardless of what's written in the file), so it was reverted back to `0.2.0rc15`. Separately, `muse tag add` enforces strict SemVer 2.0, which *requires* the hyphen before a pre-release identifier (`v0.2.0-rc15` is valid, `v0.2.0rc15` is rejected with `"Version tag must start with 'v' ... is not valid semver"`). These two canonical forms are incompatible at the string level — there is no single literal that satisfies both PEP 440's canonical form and SemVer 2.0's required hyphen. `pyproject.toml` stays the single source of truth in PEP 440 form (see decision above); the SemVer tag needs to be *derived* from it, not hand-typed as a second, independently-maintained version string. ## Goal Whenever a release is tagged with `muse tag add`, the SemVer-hyphenated tag string is computed from `pyproject.toml`'s PEP 440 version automatically — no one hand-types `v0.2.0-rc15` and risks a typo or drift from the actual shipped version. ## Suggested approach A small helper (Python, using the already-available `packaging` library) that takes a PEP 440 version string and emits the SemVer-compatible tag: ```python from packaging.version import Version def pep440_to_semver_tag(version_str: str) -> str: v = Version(version_str) pre = f"-{v.pre[0]}{v.pre[1]}" if v.pre else "" return f"v{v.major}.{v.minor}.{v.micro}{pre}" # pep440_to_semver_tag("0.2.0rc15") -> "v0.2.0-rc15" ``` Wire this into the standard release flow (documented in `musehub/.muse/agent.md`'s "Standard release flow" section) as a step after `publish_muse_release.sh` succeeds: ```bash TAG=$(python3 -c "from packaging.version import Version; v=Version('$MUSE_VERSION'); pre=f'-{v.pre[0]}{v.pre[1]}' if v.pre else ''; print(f'v{v.major}.{v.minor}.{v.micro}{pre}')") muse tag add "$TAG" --message "release $MUSE_VERSION" ``` ## Deliverables - [ ] Helper function (or inline one-liner, whichever fits the existing script style better) added to the release flow, not hand-typed. - [ ] `musehub/.muse/agent.md`'s "Standard release flow" section updated with the new tagging step. - [ ] Manually QA'd once against a real release before considering this done (tag the current `0.2.0rc15` release once MWP verb QA — muse#63 — is fully signed off, per gabriel's explicit sequencing: manual QA first, then tag). ## Out of scope - Changing `pyproject.toml`'s format — settled as PEP 440, no hyphen, per the incident above. - Retroactively tagging any release prior to this ticket.