Repository navigation
Distribution gate: install the built wheel and sdist, then run them - #1012
Merged
1 commit merged intoSep 16, 2026
Merged
1 commit merged into
1 commit merged into
Conversation
CI installs with `pip install -e .`, which reads every file straight out of the checkout and never exercises the build backend. Nothing in the suite could catch a file that fails to reach the wheel; that is how the shipped skills went missing from the wheel in #909 while `test_qoder_bundled_skills` stayed green, because its editable-install fallback resolved to the checkout's own `skills/` directory. tests/test_packaging.py builds a wheel and an sdist with `python -m build`, installs each into its own throwaway virtual environment, and drives the installed program from a working directory unrelated to this checkout: `--version`, `build` over a small real repository assembled from this project's own sources, `status --json`, `detect-changes`, `visualize` (the only reader of the vendored D3 asset), and the MCP server over stdio spoken to with the real `mcp` client. Each smoke test first asserts the installed package resolves inside the environment's site-packages and not under the repository root, so a pass can never come from the source tree leaking in. The inventory of data files the wheel must carry is derived from the code rather than from the pyproject include list: every packaged module is parsed and its `importlib.resources.files("code_review_graph")` and `Path(__file__).parent` expressions are statically evaluated. `test_discovery_finds_the_known_data_surface` is the canary for that derivation, so a broken evaluator fails loudly instead of leaving the inventory check asserting nothing. Also checked: the wheel carries no tests, dot-directories, agent config files or databases; the sdist's only dot-path is the `.gitignore` hatchling cannot suppress; version agrees across `__init__`, pyproject and both artefacts; the declared Python floor both parses and installs on a real interpreter of that version; entry points import and are callable; and the core path runs with no optional-extra distribution present. The marker is registered in pyproject and skipped by default by a hook in tests/conftest.py. Run it with: uv run --python 3.13 python -m pytest tests/test_packaging.py -m packaging Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
code-review-graph reviewOverall risk: 0.40 (MEDIUM) — 40 changed function(s)/class(es), 0 affected flow(s), 15 test gap(s) Risk-scored changes
Test gaps
Token savings: this graph-backed report used ~10,892 fewer tokens (~65%) than reading every changed file in full (estimated, chars/4 approximation). Powered by code-review-graph — local-first analysis; no code leaves the CI runner. |
pull Bot
pushed a commit
to bhardwajRahul/code-review-graph
that referenced
this pull request
Sep 17, 2026
The four release checks built as PRs tirth8205#1012, tirth8205#1013, tirth8205#1014 and tirth8205#1016 each run on demand and gate nothing. Alongside them, the end-to-end MCP client tests, the browser tests and the coverage matrix already run on every pull request but are not required checks, so nothing they say has ever stopped a promotion. This wires all seven into one workflow that runs when code lands on `testing`, which is the branch a release is cut from. Not on pull requests. Every job here takes minutes and a contributor must not wait for them; ci.yml is untouched. What blocks and what only reports is decided per check and justified in CONTRIBUTING.md. Blocking: upgrade-path (a database a user already has is not ours to lose), packaging (a broken wheel reaches everyone at once), determinism (no network, so a failure is a real difference), the coverage matrix, and the end-to-end tests on Linux and macOS. Reporting: the pinned real-repository corpus (it clones eight repositories that belong to other people, and a failed clone must not stop a release), the browser tests, and the Windows end-to-end leg. Reporting is not ignoring. scripts/promotion_gate.py wraps every check and fails it when the check did not actually run: its test module is not in the checkout, the selection collected almost nothing, or the tests were skipped by an opt-in hook instead of executed. Three of the four new checks are opt-in through a pytest_collection_modifyitems hook in tests/conftest.py and those hooks arrive on four branches that overwrite one another on merge, so a bare `pytest -m corpus` step exiting 0 on twenty skipped tests is a real prospect, not a hypothetical. The result is posted once to the job summary and once to the open issue labelled promotion-gate, naming every check and, for a failure, the assertion that moved. The gate opens no pull request and merges nothing. tests/test_promotion_gate.py asserts the policy, drives each canary to failure with a real junit report, and checks the workflow against the registry so a check cannot be added to one and forgotten in the other. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
CI installs this project with
pip install -e .. That reads every filestraight out of the checkout and never runs the build backend, so nothing in
the suite can catch a file that fails to reach the wheel.
That is exactly how #909 happened: the shipped skills were missing from the
wheel while
tests/test_qoder_bundled_skills.pystayed green, becauseinstall_qoder_skillsfalls back toPath(__file__).parent.parent / "skills"and under an editable install that fallback resolves to the checkout's own
skills/directory. The test could not tell a working wheel from a broken one.What this adds
tests/test_packaging.py, markedpackagingand skipped by default.python -m buildinto a temp directory.installed program from a working directory unrelated to this checkout:
--version,buildover a small real repository assembled from thisproject's own source files,
status --json,detect-changes --base HEAD~1,visualize(the only path that reads the vendored D3 asset), and the MCPserver over stdio spoken to with the real
mcpclient(
initialize,tools/list,prompts/list, and atools/callthat readsthe packaged LLM reference document).
a different path.
derived by parsing every packaged module and statically evaluating its
importlib.resources.files("code_review_graph")andPath(__file__).parentexpressions, rather than by trusting the pyproject include list. It resolves
code_review_graph/assets/d3.v7.min.js,code_review_graph/docs/LLM-OPTIMIZED-REFERENCE.md,code_review_graph/eval/configs/*.yamland thecode_review_graph/_bundled_skillsresource, and requires the bundled skillset to equal the checkout's
skills/tree exactly.__init__,pyproject.toml, the wheelMETADATAand both filenames; the declaredPython floor matches pyproject, parses with
ast(feature_version=...), andis installed onto a real interpreter of that version where every packaged
module is imported; both console scripts exist, import and are callable;
and the core path runs with no optional-extra distribution installed.
databases or scratch trees, and that the sdist's only dot-path is the
.gitignorehatchling cannot suppress.Every smoke test first asserts the installed
code_review_graphresolvesinside the environment's
site-packagesand not underREPO_ROOT, so apass can never come from the source tree leaking in.
Canary
test_discovery_finds_the_known_data_surfaceguards the derivation theinventory check depends on. If the static evaluator stops finding path
expressions, the inventory test would assert nothing and pass; the canary fails
instead, naming what it did and did not find.
Proof of teeth
Each class of check was broken and the failure recorded, then restored.
Removed
[tool.hatch.build.targets.wheel.force-include]from pyproject,reproducing watch ignore snapshot, partial-overlap root guard, and Qoder skills missing from wheel installs #909:
Force-included a
tests/test_leak.pyand a.beads/beads.dbinto the wheel,and bumped
__version__to 2.4.0:Moved
code_review_graph/assets/d3.v7.min.jsout of the package:Numbers
code_review_graph-2.3.8-py3-none-any.whl: 658,602 bytes, 92 filescode_review_graph-2.3.8.tar.gz: ~670,000 bytes, 107 filesgit-tracked contents of
code_review_graph/plus the seven bundledSKILL.mdfiles anddist-info.Running it
Skipped by a hook in
tests/conftest.pyunless the marker expression names itor
CRG_RUN_PACKAGING_TESTS=1is set, sopytest tests/is unaffected.Gates
uv run --python 3.13 python -m pytest tests/ -q— 3383 passed, 26 skipped, 2 xpasseduv run ruff check code_review_graph/— clean (and on the new test files)uv run --with mypy mypy code_review_graph/ --ignore-missing-imports --no-strict-optional— cleanpytest tests/test_packaging.py -m packaging— 16 passedFindings, not fixed here
Reported separately; no production code is changed by this PR.
🤖 Generated with Claude Code