Skip to content

rebase PR #341 closure-class gate fix onto master after #340 deleted in_import_error_except - #343

Closed
jaylfc wants to merge 1 commit into
masterfrom
exec/tsk-ja3pr4
Closed

rebase PR #341 closure-class gate fix onto master after #340 deleted in_import_error_except#343
jaylfc wants to merge 1 commit into
masterfrom
exec/tsk-ja3pr4

Conversation

@jaylfc

@jaylfc jaylfc commented Aug 18, 2026

Copy link
Copy Markdown
Owner

CARD TITLE (intent, not commit subject): rebase PR #341 closure-class gate fix onto master after #340 deleted in_import_error_except

Autonomous build of board card tsk-ja3pr4.

The ClassDef traversal guarded on if " > " not in scope:, so any class
defined inside a closure (scope module > outer) was never visited and
its methods were never collected. A redefined class within one closure
was therefore invisible.

Descend unconditionally and qualify the class scope with the enclosing
closure (module > factory > class Foo) so a redefined class in one closure
still collides, while the same class name in different closures does not.
Module-level and nested-class scopes are unchanged, and the if/elif/else
and try/except arm behaviour is untouched.

The in_import_error_except parameter from the original #341 diff is
dropped because #340 already removed it.

Verified: seven-case probe table all OK, collection probe 2/2/2, swap check
3 failed against master gate, full suite 1557 passed 12 skipped.

Files:
changelog.d/tsk-ja3pr4-closure-class-scan.md | 2 +
changelog.d/tsk-lvxars-closure-class-scan.md | 2 +
scripts/normalise_handle_gate.py | 36 +++++----
tests/test_normalise_handle_gate.py | 106 +++++++++++++++++++++++++++
4 files changed, 131 insertions(+), 15 deletions(-)

The ClassDef traversal guarded on `if " > " not in scope:`, so any class
defined inside a closure (scope `module > outer`) was never visited and
its methods were never collected. A redefined class within one closure
was therefore invisible.

Descend unconditionally and qualify the class scope with the enclosing
closure (`module > factory > class Foo`) so a redefined class in one closure
still collides, while the same class name in different closures does not.
Module-level and nested-class scopes are unchanged, and the if/elif/else
and try/except arm behaviour is untouched.

The `in_import_error_except` parameter from the original #341 diff is
dropped because #340 already removed it.

Verified: seven-case probe table all OK, collection probe 2/2/2, swap check
3 failed against master gate, full suite 1557 passed 12 skipped.
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@jaylfc, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 1 minute

Limit details: You’ve used all 1 included review currently available under your plan.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 09843c69-1010-449b-8a85-8dc3f5bcc2c6

📥 Commits

Reviewing files that changed from the base of the PR and between 12d7710 and c192947.

📒 Files selected for processing (4)
  • changelog.d/tsk-ja3pr4-closure-class-scan.md
  • changelog.d/tsk-lvxars-closure-class-scan.md
  • scripts/normalise_handle_gate.py
  • tests/test_normalise_handle_gate.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gitar-bot

gitar-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

@jaylfc

jaylfc commented Aug 18, 2026

Copy link
Copy Markdown
Owner Author

BLOCK — but the rebase you were asked for is CORRECT and COMPLETE.

The job this card was filed for is done. Verified, not assumed:

in_import_error_except      absent from this branch AND from master   ← the #340 conflict is resolved
conflict markers            clean
normalise_handle_gate.py    clean over the whole repo (real CLI, not the helper)
check_deleted_symbols.py    deleted-symbols-guard: clean  (output read, not exit code)
check_witness_token.py      clean
full suite on trial merge   1557 passed, 12 skipped = baseline 1552 + exactly the 5 new tests

And the new tests genuinely discriminate — the thing #340 got wrong. I ran the new test file unchanged
against master's old gate, with md5 proving the swap (test becac23d both sides; gate 9308ff0c old vs
584c85ff new):

NEW tests vs OLD gate    3 failed, 46 passed   ← discriminates
NEW tests vs NEW gate    49 passed             ← control

The block is for a false positive in the new scoping rule, plus a duplicated changelog. Revision card
tsk-odrncg carries every measurement below.


BLOCKER 1 — the new rule false-positives on a class defined inside a METHOD

This one is inherited from #341, and I missed it there. My #341 review said "the work is correct" and
blocked only on the conflict. That statement was wrong, and I am correcting it here rather than quietly.

My probes covered the closure shapes the PR named and not the adjacent one — which is exactly how it
survived two reviews.

Measured against all three implementations, with controls:

CASE                                        master   #341                #343    PROPOSED FIX
A class-in-a-METHOD vs module-level class   silent   FIRES class Foo/run FIRES    silent
B same class name in two sibling methods    silent   FIRES class Foo/run FIRES    silent
C ctl class-in-module-FUNCTION vs module    silent   silent              silent   silent
D ctl redefined class in one closure        silent   FIRES (intended)    FIRES    FIRES   ← kept
E ctl module-level nested-class duplicate   FIRES    FIRES               FIRES    FIRES   ← kept

Case A — the gate calls this a duplicate run at lines 4 and 6, though the two are in unrelated scopes:

class Outer:
    def make(self):
        class Foo:
            def run(self): ...
class Foo:
    def run(self): ...

Cause. The ast.FunctionDef branch recurses into a closure with class_path=(), so the path is reset,
while scope becomes "class Outer > make". The new guard is
if scope.startswith("class ") or scope == "module": — and "class Outer > make" does start with
"class ", so it takes the dotted branch and builds the bare scope "class Foo" out of the emptied
class_path. That is indistinguishable from a genuine module-level class Foo.

Severity, stated honestly: latent, not live. I ran the gate over the whole repository and it is clean.
All 15 classes-defined-in-a-function-body here sit inside module-level functions (test helpers plus
taosmd/service.py::_fetcher and taosmd/http_server.py::_make_handler), never inside a method. Nothing
breaks today. It is still a false positive in a merge gate, waiting on the first person who writes one.

Proposed fix, already verified — re-run it rather than trusting me:

if scope == "module" or " > " not in scope:

A pure class nest never contains " > " ("class Foo", "class Outer.Inner"); a method's closure always
does ("class Outer > make"). With that single line changed: A and B go silent, D and E still fire, C stays
silent, all 49 of this PR's own tests still pass, and the gate stays clean over the repo.

Tests for A and B need to exist, in the scope-parity corpus. Their absence is the actual root cause here.

BLOCKER 2 — the changelog entry ships twice

changelog.d/tsk-ja3pr4-closure-class-scan.md    blob 66de3dd9
changelog.d/tsk-lvxars-closure-class-scan.md    blob 66de3dd9    ← byte-identical

tsk-lvxars-closure-class-scan.md is not on master — #341 was blocked and closed, so it never landed.
Shipping both puts the same "Fixed" line in the changelog twice. Keep the tsk-ja3pr4 one.

FIX 3 — the docstring rewrite weakens a sentence #340 deliberately made precise

master:  … fallback patterns are therefore left silent by the same sibling-arm rule,
         since at most one arm ever binds.

#343:    … fallback patterns are recognised and left silent.

"recognised" reads as special-case handling — precisely the in_import_error_except flag #340 deleted. The
silence now comes from the generic sibling-arm rule, and master's wording says so. Since this PR exists
because of that deletion, restore master's phrasing.

FIX 4 — stray space in the docstring terminator

The closing triple-quote on this branch carries a leading space where master has none. Cosmetic, but it
changes the docstring content.


Closing under the close-on-block policy; the branch is preserved. Revision card: tsk-odrncg.

@jaylfc jaylfc closed this Aug 18, 2026
jaylfc added a commit that referenced this pull request Aug 18, 2026
…inside methods (#350)

Revision of #343. Fixes a false positive inherited from #341 that I missed in
my own review of it: a class defined inside a METHOD collapsed to the bare scope
`class Foo` and collided with a real module-level class of the same name,
because the FunctionDef branch resets class_path while the scope still starts
with "class ".

Fix is the one-line guard `if scope == "module" or " > " not in scope:`.

16 scope shapes, each measured against master, the pre-fix rebase commit
e6b270a, and this HEAD:

    SHAPE                                    master   e6b270a   PR350   want
    CTL module fn dup                          FIRE     FIRE      FIRE   FIRE
    CTL method dup in one class                FIRE     FIRE      FIRE   FIRE
    CTL no dup at all                        silent   silent    silent  silent
    CTL same method name, 2 module classes   silent   silent    silent  silent
    class-in-METHOD vs module class          silent   *FIRE*    silent  silent
    class-in-METHOD, dup method inside       silent     FIRE      FIRE   FIRE
    same class-in-method in 2 methods        silent   *FIRE*    silent  silent
    class-in-CLOSURE vs module class         silent   silent    silent  silent
    class-in-closure dup method              silent     FIRE      FIRE   FIRE
    same class-in-closure, 2 closures        silent   silent    silent  silent
    nested class, same method name           silent   silent    silent  silent
    nested class dup method                    FIRE     FIRE      FIRE   FIRE
    closure fn dup in one parent               FIRE     FIRE      FIRE   FIRE
    same closure fn, 2 parents               silent   silent    silent  silent
    if/else sibling arms                     silent   silent    silent  silent
    try/except ImportError fallback          silent   silent    silent  silent
                                mismatches:     2        2         0

master misses two real duplicates; e6b270a gains those and adds two false
positives; this HEAD keeps both gains with zero mismatches. The second false
positive (same class name in two sibling methods) was not on the card and is
fixed here too.

New tests proven non-vacuous by running them unchanged against the OLD gate
(both copies md5-verified): 3 failed / 48 passed, failing exactly
test_class_in_method_does_not_collide_with_module_class,
test_same_class_in_two_sibling_methods_is_silent and test_scope_parity_corpus.

Suite 1561 passed / 12 skipped = 1554 baseline + exactly the 7 new test
functions (test_normalise_handle_gate.py 44 -> 51). Re-measured on the new
master after my own #344 merge moved it. All three gates clean.

Duplicate changelog fragment (tsk-lvxars) removed; an md5 sweep of changelog.d
finds no byte-identical pair. ImportError docstring sentence restored byte-exact.

STATED LIMITATION, carded as tsk-sgia5s rather than smoothed over: the stray
docstring space FIX 4 asked about was not removed, it moved up one line onto the
new `function body.` continuation. The closing triple-quote is clean, so the
acceptance item as written is satisfied; master, the control, has no leading
space on any docstring line.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant