Fixed
-
The crash that restarted the container is gone, and it was a version. The
parser core wastree-sitter0.26 while every grammar wheel here is built against
0.25, and the mismatch showed up where the grammar's field tables are read: the tree
walk touched node fields through a stale view of the language and, under memory
pressure, the process died with SIGSEGV —Error 139in the container, which no
Pythonexceptcan catch and which arrives as a bare restart. Once the heap was
poisoned on purpose it reproduced every time (PYTHONMALLOC=malloc MALLOC_PERTURB_=165): six crashes in six runs on 0.26.0, none in eight on 0.25.2,
while plain parsing never crashed on either — which is what placed the fault in the
walk and not in the grammar. The core is held below 0.26 until the wheels move, and
run_prodsetsPYTHONFAULTHANDLER=1, so a crash of any kind now leaves a Python
stack in the container log instead of an exit code on its own. -
The image builds again. Three causes, in the order they surfaced.
uv sync --lockedran in a layer holding only the metadata, and building the project needs
its own source, so the install failed withExpected a Python module at: src/revu/__init__.py; the first sync now passes--no-install-projectand the
project is installed once its source has been copied in. The certificate step then
failed the same way throughuv run, which syncs — and therefore builds — before
runningcertifi; it uses the environment's own interpreter now. Anduvcame from
ghcr.io/astral-sh/uv:latest, so a new uv could change how this image builds
without a line in the repository changing, which is exactly what had happened: the
uv_buildpin was written against an older uv. The image tag and the backend range
are pinned together now. -
A version tag is released, and only once. The release workflow listened for
v*while every tag in this repository is bare (1.0.0through1.5.0), so
git push origin 2.4.0started nothing at all; and the release step rebuilt the tag
name from the version, so pushing2.4.0would have releasedv2.4.0, creating it
if it did not exist. Both spellings are accepted now, and the tag that was pushed is
the one released. The image was also built twice — once by the push tomain, once
by the tag that followed — sobuild.yamlis now one workflow for both, and the tag
path verifies the image themainpush published instead of rebuilding it. Its lint
step ran nothing either:--hook-stage manualselects hooks declaring that stage,
and none here do. -
Gitea's conversations are read through Gitea's own routes.
GET /repos/{owner}/{repo}/pulls/{index}/commentsdoes not exist in Gitea's API —
it is GitHub's route, inherited by sharing the GitHub-compatible comment reader —
and it answers 404. The review survived that by running without the history, which
is what made it quiet: a repeated review on Gitea never saw its own earlier inline
findings, so the feature that makes a second review build on the first never worked
there. The routes are a per-host hook now, and Gitea reads its issue timeline (an
inline review comment is an issue comment carrying apull_request_url) plus
/pulls/{index}/reviewsfor the review bodies. -
Deep context no longer drops a file without saying so. The tree walk was
recursive, and a generated tree is as deep as its source is nested: a call chain
a thousand calls long exhausted the interpreter's stack,parseswallowed the
RecursionErrorand returned an empty result — indistinguishable, at the call
site, from a file with nothing in it. A file with 900 nested calls parsed to zero
symbols before this; the walk is iterative now and the same file yields its
function. Anything that still fails is logged with its traceback instead of
disappearing. -
The walk also holds its
Parserin a local until the walk is over.Treekeeps
its language alive, so this is belt and braces rather than a fix — but a parser is
cheap to keep and a use-after-free in a C extension cannot be caught. -
A wrapper definition (a decorator, a template) is asked through the same rule
lookup as every other node, sonested_inside_definitionandrequires_fieldare
no longer skipped for it.
Changed
- A refusal by the Git host (401, 403, 404) logs what that host's own documentation
requires of a token: Gitea's scope groups (read:repositoryfor/repos/*,
write:issueforissues/*, a scope required even for a public repository),
GitHub'srepo, GitLab'sapi. It goes to the log and not to the pull request —
scopes are the operator's business, while the author only needs to know that the
review failed.