Same slip as triggershell's equivalent fix: git mv reuses the already-
indexed blob for the source path rather than re-hashing the working
tree, so it silently ignores unstaged edits made before the rename.
The previous commit renamed release.yaml to ci.yml but carried over
the old single-job content underneath it. This is the content that
commit was supposed to carry.
Same split as pulsenode's workflow: previously this only ran at all
when pushing a version tag, so ruff/pytest never ran on regular
commits or PRs - a broken push could sit unnoticed until someone tried
to cut a release. Now checks run on every push and PR; publish to the
PyPI registry stays gated to a tag push and requires checks to pass
first. Renamed release.yaml -> ci.yml to match.
Adds ruff (lint + format check) as a dev dependency and gate, applies its
fixes across the codebase (modernized typing, safer zip usage, line
length), and adds a Gitea Actions workflow that lints, tests, builds, and
publishes to the Gitea PyPI registry on v*.*.* tag pushes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>