Files
rune/tests/test_release_recursion.py
Nick c2528c1d9d
CI Pipeline / best-practices (push) Successful in 8s
CI Pipeline / documentation (push) Successful in 4s
CI Pipeline / canary (push) Failing after 37s
CI Pipeline / lint (push) Successful in 45s
Auto-version and Release / release (push) Successful in 55s
CI Pipeline / type-check (push) Successful in 32s
CI Pipeline / gitea-offline (push) Successful in 17s
CI Pipeline / unit-tests (push) Failing after 59s
deploy / deploy (push) Failing after 15m24s
CI Pipeline / required-checks (push) Successful in 1s
feat(deploy): two-mode TUI canary (candidate scrub vs current), negative deployment regression, release-recursion invariant
Handoff 2026-09-11 Part A-C:

A. tui_startup_canary two modes: candidate (--stage, sys.path SCRUBBED of
   /mnt/workdrive/rune-code/current + imported-rune purge + contamination
   check that rune resolves from the stage) and current (consuming path).
   Scrub proven: candidate mode on the 76f71fc stage reports 76f71fc/0.66.2
   while current is bb536c9/0.66.3. FIXED a live exit-code bug: main()
   returned 1 but was called without sys.exit — a failed canary exited 0
   (the deploy gate's own returncode check in deploy_stage.py was correct,
   so promotion was never actually bypassed; the standalone script was).

B. Negative deployment canary (tests/test_deployment_canary.py): broken
   staged candidate → canary exit 1 naming the failure → current identity
   byte-identical before/after. Plus current-mode identity regression.

C. Release recursion: is_release_commit() extracted into
   version_convergence.py (ONE source of truth); workflow guard uses
   --is-release-subject CLI (no inline python in YAML); regression tests
   cover recognition, never-bump-from-release-commits (incl. the 5x loop
   case), substantive commits still bumping, and the CLI exit contract.

1241 tests green; ratchet 353/80; henxels clean.
2026-09-11 03:16:50 +01:00

88 lines
3.2 KiB
Python

"""Release recursion regression (handoff 2026-09-11 Part C).
The rule: a release bump commit MUST NOT trigger another version bump.
The decision lives in version_convergence (ONE source of truth) — the
workflow guard calls --is-release-subject; these tests exercise the same
function the workflow uses, plus the CLI exit-code contract itself.
"""
import os
import subprocess
import sys
import unittest
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO / "scripts"))
from version_convergence import is_release_commit # noqa: E402
class ReleaseRecursionRule(unittest.TestCase):
def test_release_commit_is_recognized(self):
# The EXACT subject shape the auto-version workflow generates.
self.assertTrue(
is_release_commit(
"chore(release): auto-bump version to 0.66.2 — earned by # "
"(patch: converged release (push-to-master bump))"
)
)
self.assertTrue(is_release_commit("chore(release): auto-bump version to 0.61.0"))
self.assertTrue(is_release_commit("chore: release v0.60.0"))
def test_release_commit_never_warrants_a_bump(self):
# The deeper invariant: classify_subject('other') does not feed
# highest_warranted_bump, so a release commit alone cannot bump.
from version_convergence import classify_subject, highest_warranted_bump
subject = (
"chore(release): auto-bump version to 0.66.2 — earned by # (patch: converged release (push-to-master bump))"
)
self.assertEqual(classify_subject(subject), "other")
self.assertIsNone(highest_warranted_bump([subject]))
self.assertIsNone(highest_warranted_bump([subject] * 5)) # the loop case
def test_substantive_commits_still_bump(self):
from version_convergence import classify_subject, highest_warranted_bump
self.assertEqual(classify_subject("feat(ADAPT-LIVE): route discriminator"), "feat")
self.assertEqual(classify_subject("fix(deploy): prefix-match"), "fix")
self.assertEqual(
highest_warranted_bump(["feat(x): a", "fix(y): b"]),
"minor",
)
def test_cli_exit_contract(self):
# The workflow consumes the CLI, not the Python API — prove the
# exit-code contract the guard depends on.
r = subprocess.run(
[
sys.executable,
str(REPO / "scripts" / "version_convergence.py"),
"--is-release-subject",
"chore(release): auto-bump version to 0.66.2",
],
capture_output=True,
text=True,
timeout=60,
cwd=str(REPO),
)
self.assertEqual(r.returncode, 0, "release subject → guard exits 0 (skip)")
r2 = subprocess.run(
[
sys.executable,
str(REPO / "scripts" / "version_convergence.py"),
"--is-release-subject",
"feat(x): real feature",
],
capture_output=True,
text=True,
timeout=60,
cwd=str(REPO),
)
self.assertEqual(r2.returncode, 1, "feat subject → guard exits 1 (bump)")
if __name__ == "__main__":
unittest.main()