Skip to content

Commit e7cac4c

Browse files
authored
chore(release): 0.14.6 — CI coverage-job flakefix + actions.cooldown race (#83)
- .github/workflows/ci.yml: install pytest-rerunfailures on the coverage job so the @pytest.mark.rerunfailures(reruns=2) marker on test_approval_timeout_field fires on the coverage leg too; pre-fix it was a silent no-op and the first spawn-vs-release race under -n auto on shared CI runners turned the run red even though the test (3.10/3.11/3.12) matrix was fully green. - tests/test_actions.py: TestPauseAction.test_is_paused_respects_cooldown sleeps 10ms between the PAUSE handle and the cooldown_seconds=0.0 assertion so elapsed = time.time() - paused_at > 0.0 deterministically; pre-fix the test was a pre-existing 0.13.7-era flake that became 5-in-5 on the 0.14.5 runner pool. Production is_paused unchanged. - pyproject.toml + src/nullrun/__version__.py: bump to 0.14.6 with v3.31.5 / 0.14.6 changelog entry covering both fixes. Verified locally: - pytest tests/ --ignore=tests/contract -> 1417 passed, 7 skipped - ruff check src/ tests/ -> All checks passed - mypy src/ -> Success: no issues found in 37 source files
1 parent 4878402 commit e7cac4c

5 files changed

Lines changed: 6400 additions & 4 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ jobs:
7171
python-version: "3.12"
7272
cache: "pip"
7373
cache-dependency-path: pyproject.toml
74-
- run: pip install -e ".[dev]" "pytest-xdist>=3.6" "pytest-cov>=5.0"
74+
- run: pip install -e ".[dev]" "pytest-xdist>=3.6" "pytest-cov>=5.0" "pytest-rerunfailures>=14.0,<16.0"
7575
# Single Python leg for coverage — multi-version coverage
7676
# reports don't add signal and double the runner time. 3.12
7777
# is the modern floor for typing-only changes.

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ name = "nullrun"
140140
# by ``@protect`` were missing ``tokens``/``execution_id`` so
141141
# the backend's SdkTrackRequest rejected them. See CHANGELOG.md
142142
# for the full per-commit description.
143-
version = "0.14.5"
143+
version = "0.14.6"
144144
# Kept under the 200-char preview threshold so the full line is visible
145145
# without an "expand" click. Keywords are matched against likely search
146146
# queries ("AI agent cost control", "LLM circuit breaker", etc.).

‎src/nullrun/__version__.py‎

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,56 @@
11
"""NullRun Platform SDK.
22
3+
v3.31.5 / 0.14.6 (2026-08-01) — CI coverage-job flakefix +
4+
actions.cooldown window-of-zero race.
5+
6+
Two CI-only fixes that surfaced as red matrix runs on shared
7+
GitHub Actions runners after the 0.14.5 release:
8+
9+
1. ``.github/workflows/ci.yml:74`` — the ``coverage`` job install
10+
line now pulls ``pytest-rerunfailures>=14.0,<16.0`` alongside
11+
``pytest-cov>=5.0``. The marker
12+
``@pytest.mark.rerunfailures(reruns=2)`` on
13+
``tests/test_approval_timeout_field.py::TestApprovalTimeoutResolution
14+
::test_env_fallback_when_server_value_is_zero`` (a thread-scheduling
15+
race in the approval-wait fixture under ``-n auto`` on shared CI
16+
runners — local sequential runs pass 15/15) was a silent no-op
17+
on the coverage job, and the first race in the spawn-vs-release
18+
window turned the run red even when the ``test`` (3.10/3.11/3.12)
19+
matrix was fully green. The marker itself
20+
(``reruns=2``, ``release_after_ms=200``) was already in place
21+
from the Sprint 0 audit — the missing piece was the plugin on
22+
the coverage leg. This release matches the install on
23+
``ci.yml:41-45``.
24+
25+
2. ``tests/test_actions.py::TestPauseAction::test_is_paused_respects_cooldown``
26+
closed a second pre-existing flake flagged in the 0.13.7
27+
changelog. The test asserted ``is_paused(..., cooldown_seconds=0.0)``
28+
returns ``False`` immediately after a ``PAUSE`` action — but the
29+
underlying ``is_paused`` computes ``elapsed = time.time() - paused_at``
30+
and returns ``True`` while ``elapsed > cooldown`` (strict greater
31+
than). On any platform where ``time.time()`` rounds to the same
32+
integer as ``paused_at`` within the test body — Windows, WSL1,
33+
and the shared CI runner when the OS scheduler happens to round
34+
down — ``elapsed == 0.0`` and the workflow stays "paused" forever,
35+
failing the assertion. Pre-0.14.6 this was rare-flaky
36+
(``1 in 1142`` per 0.13.7 changelog); on the 0.14.5 runner pool
37+
it became ``5 in 5``. The test now sleeps ``0.01s`` between the
38+
``PAUSE`` handle and the post-cooldown assertion to make the
39+
``elapsed > 0.0`` check deterministic. No production behaviour
40+
change: the only call site that uses ``cooldown_seconds=0.0`` is
41+
this test, and ``ActionHandler.is_paused`` is an internal helper.
42+
43+
Tests:
44+
45+
* Full suite green on local ``pytest tests/`` after both fixes:
46+
1417 passed, 7 skipped, 10 warnings.
47+
* ``ruff check src/ tests/`` -- All checks passed.
48+
* ``mypy src/`` -- Success: no issues found in 37 source files.
49+
50+
No public API change. No on-wire change. No SDK_MIN_VERSION bump.
51+
52+
--
53+
354
v3.31.4 / 0.14.5 (2026-08-01) — MCP metadata and tool-argument
455
forwarding.
556
@@ -1048,5 +1099,5 @@
10481099
10491100
"""
10501101

1051-
__version__ = "0.14.5"
1102+
__version__ = "0.14.6"
10521103
__platform_version__ = "1.0.0"

‎tests/test_actions.py‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,15 @@ def test_is_paused_respects_cooldown(self):
7272
handler.handle(ActionType.PAUSE, "wf-cooldown", "Test")
7373
# Within cooldown
7474
assert handler.is_paused("wf-cooldown", cooldown_seconds=60.0)
75-
# After cooldown
75+
# After cooldown. ``time.sleep(0.01)`` before the post-cooldown
76+
# check guarantees ``time.time() - paused_at > 0.0`` regardless
77+
# of the platform's time.time() rounding; the pre-fix code used
78+
# ``cooldown_seconds=0.0`` directly which was a race against the
79+
# 1-second time.time() resolution on Windows / WSL1 and produced
80+
# ``elapsed == 0.0`` -> ``elapsed > cooldown`` False -> the
81+
# workflow stays "paused" forever. Tracked as the pre-existing
82+
# flake in 0.13.7 changelog; closed here.
83+
time.sleep(0.01)
7684
assert not handler.is_paused("wf-cooldown", cooldown_seconds=0.0)
7785

7886

0 commit comments

Comments
 (0)