The price of a module is read before it is paid - #294
Merged
evkir merged 4 commits intoSep 23, 2026
Merged
Conversation
Three times on 2026-09-21 the boundary counters moved as modules entered [tool.mypy] files, and three times the expected numbers were corrected after the fact by rerunning the guard and reading its failure. The price was paid first and read afterwards. scripts/scope_price.py reports it first: the errors a candidate brings under the declared flags, and where the two boundary counters land with it inside. The scoped run is driven by a config file written to a temporary directory, so pyproject.toml is never edited and an interrupted run leaves no half-applied scope behind. The counters are counted from the import graph, the way the guard counts them, so the two numbers are comparable by construction rather than by luck. Measured against the tree it reports on: 104 modules, 22 crossers reaching 30, which is what the green guard asserts independently. CONTRIBUTING.md names it, since a script nobody is told to run is a producer with no consumer.
Two files count the same crossing: the boundary guard holds the numbers the scope page states, and the pricer projects where they land when a candidate moves inside. They agree today because one was written from the other, and nothing kept them agreeing. The day their import handling diverges, the price read before a change and the price charged after it differ, and only the second one reds. Four assertions, each one killing a mutant that survived the last round: the two readers return one pair of numbers; a module already inside the scope moves neither counter, so the projection must keep asking which side of the edge a target sits on; a run with no verdict line prices nothing rather than pricing a module at zero; and an error belongs to the module it names, so notes are not errors and a neighbour's errors are not the candidate's. The sibling guard is loaded by location. Importing it as tests.architecture asks the suite to be a package on sys.path, which the manifest gate reads as a third-party dependency named tests -- and the dependency it would really name is the install mode.
…-eight The scope page summarised three boundary moves as a rule: a leaf pays one counter, an entry point pays both. scripts/scope_price.py projected every undeclared module on 2026-09-23 and the moves fall into eleven classes, not two. core/cache.py refutes it plainly. It imports nothing outside the scope, so the rule calls it a leaf paying one counter, and it moves both: intel/ epss_client.py is the only module in the scope that reaches it and reaches nothing else, so declaring cache.py retires a crosser and a reached module at once. integrations/phantom_grid.py retires two crossers, which no rule about leaves permits, and core/orchestrator.py adds two reached modules while joining no crossing of its own. What the projection shows is two independent sums, which is why the observed pairs run from -2 and -1 to +1 and +5, and why the largest class is twenty modules that move nothing at all. The paragraph now states the mechanism and stops predicting the number. Two docstrings restated against the same measurement: the drift report said the scope named seventy-two individual modules and it names eighty-five, and the boundary guard opened on 95 modules and 19 crossers where the tree now has 104 and 22. Both were true when written. Neither is held by anything, so both are dated.
scripts/scope_price.py projected all 68 undeclared modules and twenty of them move neither boundary counter. Three of those carried a single error each, and each error was a missing type parameter rather than a contract to renegotiate: core/exploit_memory.py Counter -> Counter[str] bench/apps/_server.py tuple -> tuple[str, str] agents/web3/immunefi_severity.py dict -> Dict[str, Any] Every parameter was read off the code that fills it: the counter is built from lowercased tokens, the route key is assembled as (method, path) with both halves str, and to_dict returns Dict[str, Any]. Priced before the fact at zero errors and +0 on both counters, and the tree agrees afterwards: Success on 107 rather than 104, 272 errors outside the scope rather than 275, drift none, crossers and reached unmoved at 22 and 30. That is the first time the price was read before it was paid rather than corrected after.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
evkir
deleted the
feat/the-price-of-a-module-is-read-before-it-is-paid
branch
September 23, 2026 13:52
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.
Four commits:
two boundary counters land with it inside, measured through a config
file in a temporary directory so pyproject.toml is never edited.
Named by CONTRIBUTING.md, since a script nobody is told to run is a
producer with no consumer.
and the boundary guard return one pair of numbers, a module already
inside costs no movement, a run with no verdict prices nothing, and
an error belongs to the module it names.
scope page said a leaf pays one counter and an entry point pays both;
projecting all 68 undeclared modules gives eleven classes, not two.
core/cache.py is a leaf that moves both. Two stale docstring counts
restated against the same measurement.
104, 272 errors outside the scope rather than 275, drift none,
crossers and reached unmoved at 22 and 30.
Badge 2988 collected, mypy 107/172.
What this changes
How it was measured
Checklist
ruff format --check cyberai/ tests/andruff check cyberai/ tests/passpytest -W ignore::DeprecationWarning -m "not slow and not smoke"passes