Skip to content

ci: add pyrefly and move coverage reporting to Codecov - #113

Merged
bagowix merged 2 commits into
mainfrom
ci/pyrefly-codecov
Aug 1, 2026
Merged

ci: add pyrefly and move coverage reporting to Codecov#113
bagowix merged 2 commits into
mainfrom
ci/pyrefly-codecov

Conversation

@bagowix

@bagowix bagowix commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Summary

Two CI changes, both about making an existing guarantee visible instead of asserted.

pyrefly joins mypy and pyright as a third strict type checker — in the
quality matrix and in the pre-commit hooks. Three independent implementations
of the same type system disagree in the corners, and the corners are exactly
where a signature-preserving ParamSpec decorator lives; a check the other two
miss should fail before a release, not in a user's editor.

The package is clean under pyrefly's strict preset as it stands — no source
changes were needed. missing-override-decorator is the one error kind turned
off ([tool.pyrefly.errors]): typing.override is 3.12+ and the core carries no
typing_extensions dependency to backport it, so the transport overrides in
integrations/httpx2.py and integrations/requests.py cannot be decorated while
3.11 is supported. The 4 existing # type: ignore comments are honoured as
suppressions, not re-reported.

Coverage reporting moves to Codecov. The coverage job now writes
coverage.xml plus a JUnit report and uploads both, so a PR shows the per-file
coverage diff and the failing tests themselves rather than one total. The 100%
gate is unchanged and still enforced by pytest (fail_under in
pyproject.toml); the statuses in .github/codecov.yml mirror it (project and
patch at 100%). This replaces py-cov-action/python-coverage-comment-action and
the badge branch it maintained — the job no longer needs contents: write or
pull-requests: write, so every job in ci.yml now runs on contents: read.

secrets.CODECOV_TOKEN is configured in the repository.

Test plan

  • uv run pyrefly check — 0 errors (4 suppressed)
  • uv run prek run --all-files — all hooks pass, including the new pyrefly hook
  • uv run pytest --cov --cov-report=xml --junitxml=junit.xml -o junit_family=legacy
    — 548 passed, 100.00% coverage, both reports well-formed XML
  • ruff format --check, ruff check, mypy, pyright unchanged and green
  • CI on this PR: the Pyrefly step passes on 3.11–3.14 and 3.14t, and both
    Codecov uploads succeed (fail_ci_if_error: true, so a silent failure turns the job red)

Follow-ups (not in this PR)

  • The python-coverage-comment-action-data branch is now unused and can be deleted.
  • ty has been an unused dev dependency since before this change; left alone here.

Related issues

None — CI maintenance.

pyrefly joins mypy and pyright as a third strict type checker, in the quality
matrix and in the pre-commit hooks. Three independent implementations of the
same type system disagree in the corners, and the corners are exactly where a
signature-preserving decorator lives. The package is clean under pyrefly's
strict preset; missing-override-decorator is the one error kind turned off,
since typing.override is 3.12+ and the core carries no typing_extensions
dependency to backport it.

Coverage reporting moves to Codecov. The coverage job now writes coverage.xml
plus a JUnit report and uploads both, so a PR shows the per-file coverage diff
and the failing tests themselves instead of a single total. The 100% gate is
unchanged and still enforced by pytest (fail_under); the Codecov statuses in
.github/codecov.yml only mirror it. This replaces py-cov-action and its badge
branch, so the job drops contents: write and pull-requests: write.
@codspeed-hq

codspeed-hq Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 21 untouched benchmarks


Comparing ci/pyrefly-codecov (014dae0) with main (e06560f)

Open in CodSpeed

ty was never wired into CI or the pre-commit hooks — pyrefly is the third
checker that actually runs, so the dependency has no reader.

Codecov stayed silent on this PR: `require_changes: true` suppresses the
comment when coverage does not move, and at a flat 100% it never moves.
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

ℹ️ You can also turn on project coverage checks and project coverage reporting on Pull Request comment

Thanks for integrating Codecov - We've got you covered ☂️

@bagowix
bagowix merged commit 045ebf3 into main Aug 1, 2026
14 of 15 checks passed
@bagowix
bagowix deleted the ci/pyrefly-codecov branch August 1, 2026 18:22
bagowix added a commit that referenced this pull request Aug 1, 2026
## Summary

The `RuleBasedStateMachine` from #106 (#112) shrank a counterexample on
`main`.
It reproduces locally but not in CI — hypothesis found it on a seed CI
has not
drawn yet, so today it is a latent flake rather than a red build:

```
config: minimum_number_of_calls=1, failure_rate_threshold=1.0,
        permitted_calls_in_half_open=1, max_concurrent_probes=1
admit → settle(FAILURE)        # CLOSED → OPEN
advance(1.0)                   # the open wait elapses
admit → settle(FAILURE)        # HALF_OPEN probe fails → OPEN
```

At the last step the machine still holds `_probes_admitted == 1` while
the model
predicted `0`: the model forgets the probe round on *every* transition,
and the
machine clears it on entry to `HALF_OPEN` (`_to_half_open`) and `CLOSED`
(`_close`), not on `_open()`.

**The machine is right.** The counters are read only by `_admits_probe`,
which
is reachable only in `HALF_OPEN`, and `HALF_OPEN` is only ever entered
through
`_to_half_open()` — which resets them. Adding a reset to `_open()` would
be
defensive code written for a test, not for a caller. What the model
asserted
there was an internal detail the contract does not promise.

So `probe_budget_holds` now compares the machine's counters against the
model
only in `HALF_OPEN`, where the budget is spent and the contract defines
it; the
cap assertions (`max_concurrent_probes`, `permitted_calls_in_half_open`)
stay
unconditional. No production code changes.

## Test plan

- [x] `uv run pytest tests/test_state_machine_model.py` passes, plus 6
explicit
      `--hypothesis-seed` runs
- [x] **Mutation check — the invariant still bites.** With the
      `self._probes_in_flight -= 1` line deleted from
`StateMachine._record_probe` (a leaked probe slot), the scoped invariant
fails within one run at the same assertion. Scoping removed the false
      alarm, not the sensitivity.
- [x] The shrunk sequence is pinned as
`test__probe_budget__failed_probe_reopens__next_round_starts_full`, as
`tests/CLAUDE.md` requires — it asserts what *is* promised: the next
probe
      round starts from a full budget.
- [x] `uv run pytest --cov` — 549 passed, 100.00%
- [x] `uv run prek run --all-files` clean

## Related issues

Follow-up to #106 / #112. Independent of #113.
@bagowix bagowix mentioned this pull request Aug 3, 2026
7 tasks
bagowix added a commit that referenced this pull request Aug 3, 2026
## Summary

Prepare the `2.1.4` patch release.

- bump the package version from `2.1.3` to `2.1.4`
- move the current changelog entries from `[Unreleased]` into `2.1.4`
- update changelog comparison links
- update the release version in the comparison page
- regenerate `docs/llms-full.txt`

Everything in `[Unreleased]` is infrastructure, documentation and bug
fixes —
the correctness docs page, the OpenSSF badge, `griffe check`, mutation
testing,
pyrefly, the Codecov move, the supply-chain hardening, plus the
coordinator
lane `task_done()` fix and `EventListener` failure isolation. No public
symbol
was added or changed, so this is a patch, not a minor.

### Why now

The `v2.1.4` tag was pushed while `interlock/version.py` still read
`2.1.3`, so
the release workflow built `2.1.3` artifacts and PyPI rejected the
upload —
that filename already exists and PyPI never allows reuse. The tag has
been
deleted locally and on the remote; it needs to be re-created on the
merge
commit of this PR so the build picks up the bumped version.

### Note on `docs/llms-full.txt`

The regenerated file carries one change that is not from the version
bump:
#87 changed the migration-guide anchors in `docs/migration.md` but did
not
regenerate `llms-full.txt`, so the committed copy had drifted. Running
`scripts/build_llms_full.py` brings it back in sync.

`docs/comparison.md` keeps its "as of July 2026" label: that date
records when
the *other* libraries' versions were checked, and only interlock's own
cell
moves — the same way the 2.1.3 release handled it.

### The griffe gate had to be fixed first

`griffe check` failed this PR on `Attribute value was changed: '2.1.3'
-> '2.1.4'`.
The job landed in #118 and this is the first release since, so it had
never run
against a version bump — it would have failed every release from now on,
or
forced a misleading `breaking-change` label onto each one.

The `-f github` run still annotates the diff; the gate now reads a
`oneline`
run with that single finding filtered out. The filter matches only that
attribute, in that file, for that one check, so it fails closed: if
griffe
rewords the message it stops matching and the job fails. Verified both
ways
locally — the bump alone passes, and a removed public export
(`sync_timeout`) still fails it.

## Checklist

- [x] Test suite passes with 100% coverage (583 passed, 2 skipped)
- [x] `uv run ruff format --check` and `uv run ruff check` pass
- [x] `uv run mypy`, `uv run pyright` and `uv run pyrefly check` pass
- [x] Documentation updated (`docs/`)
- [x] `CHANGELOG.md` `[Unreleased]` updated
- [x] Release artifacts build successfully (`interlock_cb-2.1.4`, sdist
+ wheel)
- [x] Commits follow Conventional Commits

## Related issues

- Includes changes from #83, #97, #102, #103, #104, #105, #107, #113,
#87
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant