test(rules): locate the pre-tool arm by what it does, not by its name (#4633)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 52s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m48s
CI & Build / Build & push image (push) Successful in 26s
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / integration (push) Successful in 52s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m48s
CI & Build / Build & push image (push) Successful in 26s
CI run 712 failed test_neither_rule_arm_logs_its_call_behind_a_results_guard
with "min() iterable argument is empty": the guard walked the function named
build_tool_rule_hint, which since fd2ebf4 is a wrapper that searches nothing
— the arm's body moved to _tool_rule_hint. The property it guards (the call
is logged before any early return) still held; the guard had pinned a name.
It now finds the async function that calls semantic_search_rules under the
pre_tool_rule source, so a later rename moves the guard with the arm instead
of emptying it (rule 167).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -908,9 +908,23 @@ def test_neither_rule_arm_logs_its_call_behind_a_results_guard():
|
|||||||
#
|
#
|
||||||
# The property is positional: between the search and the first guard that
|
# The property is positional: between the search and the first guard that
|
||||||
# can return early, the call row has already been written.
|
# can return early, the call row has already been written.
|
||||||
|
# The arm's BODY, which since #4633 lives in `_tool_rule_hint`; the public
|
||||||
|
# `build_tool_rule_hint` is a wrapper that adds the via-lesson step and
|
||||||
|
# searches nothing itself. Located by what it does — the function that
|
||||||
|
# calls the rule search under the pre_tool_rule source — so the next
|
||||||
|
# rename moves the guard with it instead of emptying it.
|
||||||
fn = next(
|
fn = next(
|
||||||
n for n in ast.walk(ast.parse(pc_src))
|
n for n in ast.walk(ast.parse(pc_src))
|
||||||
if isinstance(n, ast.AsyncFunctionDef) and n.name == "build_tool_rule_hint"
|
if isinstance(n, ast.AsyncFunctionDef)
|
||||||
|
and any(
|
||||||
|
isinstance(c, ast.Call) and getattr(c.func, "id", None) == "semantic_search_rules"
|
||||||
|
for c in ast.walk(n)
|
||||||
|
)
|
||||||
|
and any(
|
||||||
|
isinstance(k, ast.keyword) and k.arg == "source"
|
||||||
|
and isinstance(k.value, ast.Constant) and k.value.value == "pre_tool_rule"
|
||||||
|
for k in ast.walk(n)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
search_at = min(
|
search_at = min(
|
||||||
n.lineno for n in ast.walk(fn)
|
n.lineno for n in ast.walk(fn)
|
||||||
@@ -929,7 +943,7 @@ def test_neither_rule_arm_logs_its_call_behind_a_results_guard():
|
|||||||
and any(isinstance(b, ast.Return) for b in n.body)
|
and any(isinstance(b, ast.Return) for b in n.body)
|
||||||
]
|
]
|
||||||
assert bailouts, (
|
assert bailouts, (
|
||||||
"no early return found after the search in build_tool_rule_hint — the "
|
"no early return found after the search in the pre-tool arm — the "
|
||||||
"guard has nothing left to protect, which means this test is now "
|
"guard has nothing left to protect, which means this test is now "
|
||||||
"passing vacuously rather than the arm being correct"
|
"passing vacuously rather than the arm being correct"
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user