fix(ci): stop the blocking-call ratchet taxing extracted test files, and unred main

`Lint` is failing on `origin/main` at "Check blocking-calls budget" (run
35126167644, sha 9a674f47f). The check is `continue-on-error` on `pull_request`
and fatal on `push`, so #6096 went green as a PR and turned `main` red the
moment it merged — the exact asymmetry that lets a red `main` go unnoticed.

Two separate things were wrong, and only one of them was a budget question.

**The ratchet could not see whole-file test modules.** Its docstring says it
skips test code, and `file_counts` does recognise `#[test]`, `mod tests` and
`fn test_*` — but a file that is *entirely* a test module knows nothing about
it, because that fact lives in the parent's `#[cfg(test)] mod foo;`
declaration. So extracting a test suite into its own file made untouched call
sites look new. `session_export_regression_tests.rs` and
`session_export_surface_tests.rs` are declared `#[cfg(test)]` at
`commands/mod.rs:36-41`; they were counted anyway. `cfg_test_module_files()`
now resolves those declarations and excludes the files they name.

That is a tightening, not a loosening. Six existing test files
(`telemetry/src/tests.rs`, `dsh/tests.rs`, `skills/tests.rs`,
`test_support.rs`, `subagent/tests.rs`, `golden_harness.rs`) were carrying
budgets totalling 27 sites that the ratchet should never have been counting.
Those entries are gone, so 27 sites can no longer hide inside an allowance.

**The remaining two sites are a relocation, not new debt.**
`commands/groups/session/export.rs` was budgeted `std_fs: 4`; #6096 moved those
four verbatim into `commands/session_export_host.rs`, which had no entry. Same
four calls, same synchronous callers — `SessionExportAdapter`'s trait methods at
`contract.rs:1549/1563/1567` are `fn`, not `async fn`. The budget moves with the
code. `crates/memory/src/main.rs:371` is one `std::fs::canonicalize` inside
`fn main()`, which the ratchet's own docstring names as fine ("synchronous entry
points").

Audited the regenerated JSON rather than trusting `--update`: two entries added
(both verified above), seven removed, **zero existing budgets raised**, 181 files
down to 176.

Two tests pin the new behaviour — a `#[cfg(test)] mod` declaration excludes the
file it names, and a plain `mod` declaration does not, so the exclusion cannot
quietly widen to production code.

Tests: `python3 scripts/test_check_blocking_calls_budget.py` — Ran 11 tests, OK
(9 before). `python3 scripts/check-blocking-calls-budget.py` — "600 sites across
176 files, within budget", exit 0, where it exited 1 with four offenders before.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Signed-off-by: CodeWhale Bot <[email protected]>
This commit is contained in:
CodeWhale BotandClaude Opus 5 committed 2026-09-16 10:44:53 -07:00
1 parent 223613766b
commit 478fb2907a
3 files changed
+93 -21

No files matched your search

+6 -21
View File
@@ -64,6 +64,9 @@
"crates/mcp/src/test_support.rs": {
"std_fs": 3
},
"crates/memory/src/main.rs": {
"std_fs": 1
},
"crates/models/src/model_catalog.rs": {
"std_fs": 1
},
@@ -76,9 +79,6 @@
"crates/telemetry/src/envelope.rs": {
"std_fs": 2
},
"crates/telemetry/src/tests.rs": {
"std_fs": 3
},
"crates/tui/examples/zz_perf_probe.rs": {
"std_fs": 1
},
@@ -115,12 +115,12 @@
"crates/tui/src/commands/groups/project/init.rs": {
"std_fs": 13
},
"crates/tui/src/commands/groups/session/export.rs": {
"std_fs": 4
},
"crates/tui/src/commands/session_acceptance.rs": {
"std_fs": 6
},
"crates/tui/src/commands/session_export_host.rs": {
"std_fs": 4
},
"crates/tui/src/commands/user_commands.rs": {
"std_fs": 5
},
@@ -216,9 +216,6 @@
"crates/tui/src/integrations/dsh/receipt.rs": {
"std_fs": 5
},
"crates/tui/src/integrations/dsh/tests.rs": {
"std_fs": 13
},
"crates/tui/src/lib.rs": {
"std_fs": 44
},
@@ -304,18 +301,12 @@
"crates/tui/src/shell_dispatcher.rs": {
"std_fs": 4
},
"crates/tui/src/skills/tests.rs": {
"std_fs": 4
},
"crates/tui/src/snapshot/paths.rs": {
"std_fs": 1
},
"crates/tui/src/snapshot/repo.rs": {
"std_fs": 16
},
"crates/tui/src/test_support.rs": {
"std_fs": 2
},
"crates/tui/src/tools/file_tool/tests.rs": {
"std_fs": 1
},
@@ -360,9 +351,6 @@
"crates/tui/src/tools/subagent/mod.rs": {
"std_fs": 4
},
"crates/tui/src/tools/subagent/tests.rs": {
"std_fs": 2
},
"crates/tui/src/tools/subagent/tests/roster_routes.rs": {
"std_fs": 1
},
@@ -406,9 +394,6 @@
"crates/tui/src/tui/file_tree.rs": {
"std_fs": 1
},
"crates/tui/src/tui/golden_harness.rs": {
"std_fs": 3
},
"crates/tui/src/tui/infoline/tests.rs": {
"std_fs": 3
},
+32
View File
@@ -240,9 +240,41 @@ def file_counts(path: Path) -> dict[str, int]:
return {k: v for k, v in counts.items() if v}
CFG_TEST_MOD = re.compile(
r"#\[cfg\(test\)\]\s*(?:pub(?:\([^)]*\))?\s+)?mod\s+([A-Za-z_][A-Za-z0-9_]*)\s*;"
)
def cfg_test_module_files() -> set[Path]:
"""Files that are entire `#[cfg(test)]` modules declared by another file.
The per-file scanner recognises test scope it can see *inside* a file —
`#[test]`, `mod tests`, `fn test_*`. It cannot see that a whole file is
test-only, because that fact lives in the parent's `#[cfg(test)] mod
foo;` declaration. Extracting a test suite into its own file therefore
made an untouched call site look new (#6209 follow-up: PR #6096's
`session_export_*_tests.rs`). Excluding these keeps the ratchet's stated
contract — "not ... test code" — instead of taxing the extraction.
"""
excluded: set[Path] = set()
for path in CRATES.rglob("*.rs"):
try:
text = path.read_text(encoding="utf-8", errors="ignore")
except OSError:
continue
for name in CFG_TEST_MOD.findall(text):
for candidate in (path.parent / f"{name}.rs", path.parent / name / "mod.rs"):
if candidate.is_file():
excluded.add(candidate.resolve())
return excluded
def collect_current() -> dict[str, dict[str, int]]:
budget: dict[str, dict[str, int]] = {}
test_only = cfg_test_module_files()
for path in sorted(CRATES.rglob("*.rs")):
if path.resolve() in test_only:
continue
try:
counts = file_counts(path)
except OSError:
@@ -113,5 +113,60 @@ class BlockingCallScopeTests(unittest.TestCase):
)
class CfgTestModuleExclusion(unittest.TestCase):
"""A file that is wholly a `#[cfg(test)]` module is test code (#6149).
The per-file scanner only sees test scope declared *inside* a file, so an
extracted test suite looked like brand-new unprotected call sites even
though nothing moved onto an async path. PR #6096's
`session_export_*_tests.rs` reddened `main` this way.
"""
def _crates(self, tmp: Path, files: dict[str, str]) -> Path:
crates = tmp / "crates" / "demo" / "src"
crates.mkdir(parents=True)
for name, body in files.items():
(crates / name).write_text(body, encoding="utf-8")
return tmp / "crates"
def test_whole_file_cfg_test_module_is_excluded(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
crates = self._crates(
root,
{
"lib.rs": "#[cfg(test)]\nmod suite;\n",
"suite.rs": "fn helper() { let _ = std::fs::read_to_string(p); }\n",
},
)
original = mod.CRATES
try:
mod.CRATES = crates
excluded = mod.cfg_test_module_files()
finally:
mod.CRATES = original
self.assertIn((crates / "demo" / "src" / "suite.rs").resolve(), excluded)
def test_plain_mod_declaration_is_not_excluded(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
crates = self._crates(
root,
{
"lib.rs": "mod production;\n",
"production.rs": "fn helper() { let _ = std::fs::read_to_string(p); }\n",
},
)
original = mod.CRATES
try:
mod.CRATES = crates
excluded = mod.cfg_test_module_files()
finally:
mod.CRATES = original
self.assertNotIn(
(crates / "demo" / "src" / "production.rs").resolve(), excluded
)
if __name__ == "__main__":
unittest.main()