Skip to content

Revise PR #343: the rebase is correct, but the closure-class scoping rule false-positives on a class defined inside a METHOD (inherited from #341, missed in my review of it) and the changelog entry ships twice - #350

Merged
jaylfc merged 2 commits into
masterfrom
exec/tsk-odrncg
Aug 18, 2026

Conversation

@jaylfc

@jaylfc jaylfc commented Aug 18, 2026

Copy link
Copy Markdown
Owner

CARD TITLE (intent, not commit subject): Revise PR #343: the rebase is correct, but the closure-class scoping rule false-positives on a class defined inside a METHOD (inherited from #341, missed in my review of it) and the changelog entry ships twice

Autonomous build of board card tsk-odrncg.

Revise PR #343: the rebase itself was correct, but the new
class-scope guard false-positives on a class defined inside a method.

The guard checked scope.startswith('class '), which matches a method
closure scope ('class Outer > make') as well as a bare class scope
('class Foo'). Replace it with 'scope == module or > > not in scope',
which distinguishes the two: a pure class nest never contains ' > ',
while a method closure always does.

Also:

  • Delete the duplicate changelog fragment tsk-lvxars-closure-class-scan.md
    (byte-identical copy of tsk-ja3pr4, never landed on master).
  • Restore the ImportError/ModuleNotFoundError docstring sentence to
    master's mechanism-accurate wording about the sibling-arm rule.
  • Remove the stray leading space before the docstring terminator.
  • Add tests for the two missed cases (class-in-method vs module-level
    class, same class name in two sibling methods) to the scope-parity
    corpus and as standalone assertions.

Files:
changelog.d/tsk-ja3pr4-closure-class-scan.md | 2 +
changelog.d/tsk-odrncg-gate-method-class-scope.md | 2 +
scripts/normalise_handle_gate.py | 31 +++--
tests/test_normalise_handle_gate.py | 162 ++++++++++++++++++++++
4 files changed, 185 insertions(+), 12 deletions(-)

jaylfc added 2 commits August 18, 2026 03:01
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.
…de methods

Revise PR #343: the rebase itself was correct, but the new
class-scope guard false-positives on a class defined inside a method.

The guard checked scope.startswith('class '), which matches a method
closure scope ('class Outer > make') as well as a bare class scope
('class Foo'). Replace it with 'scope == module or > > not in scope',
which distinguishes the two: a pure class nest never contains ' > ',
while a method closure always does.

Also:
- Delete the duplicate changelog fragment tsk-lvxars-closure-class-scan.md
  (byte-identical copy of tsk-ja3pr4, never landed on master).
- Restore the ImportError/ModuleNotFoundError docstring sentence to
  master's mechanism-accurate wording about the sibling-arm rule.
- Remove the stray leading space before the docstring terminator.
- Add tests for the two missed cases (class-in-method vs module-level
  class, same class name in two sibling methods) to the scope-parity
  corpus and as standalone assertions.
@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: 32 minutes

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: 4e9948f7-4ff0-4f96-be68-a25fef342a43

📥 Commits

Reviewing files that changed from the base of the PR and between 5796bc2 and 1b6aaaf.

📒 Files selected for processing (4)
  • changelog.d/tsk-ja3pr4-closure-class-scan.md
  • changelog.d/tsk-odrncg-gate-method-class-scope.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

same sibling-arm rule, since at most one arm ever binds.
Nested classes are scanned at any depth.
Nested classes are scanned at any depth, including those defined inside a
function body.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SUGGESTION: Stray leading space in docstring continuation line

The line function body. has an unintended leading space. The PR description explicitly calls for removing this space ("Remove the stray leading space before the docstring terminator"), but the diff shows it being added instead. This breaks the visual consistency of the docstring where every other continuation line starts at column 0.

Suggested change
function body.
function body.

Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
scripts/normalise_handle_gate.py 29 Stray leading space in docstring continuation line — the line function body. has an unintended leading space that the PR description explicitly calls for removing, but the diff shows it being added instead.
Files Reviewed (4 files)
  • scripts/normalise_handle_gate.py — 1 issue (core logic is correct; class-inside-closure scoping fix works as intended)
  • tests/test_normalise_handle_gate.py — no issues (new tests correctly cover closure-class and method-class edge cases)
  • changelog.d/tsk-ja3pr4-closure-class-scan.md — no issues
  • changelog.d/tsk-odrncg-gate-method-class-scope.md — no issues

Fix these issues in Kilo Cloud


Reviewed by step-3.7-flash · Input: 99.4K · Output: 31.1K · Cached: 429.2K

@jaylfc

jaylfc commented Aug 18, 2026

Copy link
Copy Markdown
Owner Author

MERGE. The scoping fix is correct across every shape I could enumerate, and it also closes a second false positive the card never named.

This is the revision of #343, which I blocked while calling the rebase itself correct. That block was on a defect I had missed in my own review of #341 — a class defined inside a METHOD getting the bare scope class Foo. This PR fixes it.

I enumerated the scope shapes myself rather than taking the test list as the shape list

That was the exact hole that let the defect through two reviews. 16 shapes, each run against master, the pre-fix rebase commit e6b270a3, and this HEAD:

SHAPE                                    master   e6b270a3   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  <- THE DEFECT
class-in-METHOD, dup method inside       silent     FIRE      FIRE   FIRE   <- #341's gain, kept
same class-in-method in 2 methods        silent   *FIRE*    silent  silent  <- 2nd false positive
class-in-CLOSURE vs module class         silent   silent    silent  silent
class-in-closure dup method              silent     FIRE      FIRE   FIRE   <- #341's gain, kept
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 (what #341 set out to fix). e6b270a3 gains those two and introduces two false positives. This HEAD keeps both gains and has zero mismatches. The second false positive — the same class name in two sibling methods of one class — is not on the card; it was live on the rebase and is fixed here too.

The fix is the one-line guard if scope == "module" or " > " not in scope:, and the mechanism is exactly as the card described: at e6b270a3 a method's closure scope class Outer > make matched startswith("class ") while the FunctionDef branch had already reset class_path to (), so the inner class collapsed to the bare scope class Foo and collided with the real module-level one.

Two corrections to my own probe, recorded because both would have passed silently

My first harness used class Foo: pass bodies. The gate tracks function/method definitions only — a class contributes scope, never a definition — so every class-only fixture was vacuous and read as "silent" everywhere. My controls mismatching on master is what exposed it.

The second harness put the method body at the same indent as class Foo:, making those fixtures IndentationErrors. The gate skips unparseable files by design, so they came back "silent" on every implementation including the one with the known defect — an unengaged probe announcing itself as a pass, again. The final harness ast.parse()s every fixture first and reports a parse failure as a probe failure, never as a result.

The new tests are non-vacuous — proven by running them against the OLD gate

New test file taken unchanged from this branch, dropped onto e6b270a3, both copies md5-verified before the run:

3 failed, 48 passed
  FAILED test_class_in_method_does_not_collide_with_module_class
  FAILED test_same_class_in_two_sibling_methods_is_silent
  FAILED test_scope_parity_corpus

failure output: got [('class Foo', 'run', [4, 6])], want []

That is cases A and B plus the corpus, failing for exactly the right reason, and the ('class Foo', ...) in the failure independently reproduces the bare-scope collision. Acceptance item 2 asked for A and B to be in the scope-parity corpus and shown failing before the fix; both are, and they are.

The remaining acceptance items

one changelog fragment, not two    tsk-lvxars duplicate GONE; the two that remain
                                   describe different changes, and an md5 sweep of
                                   changelog.d finds no byte-identical pair at all
ImportError docstring sentence     restored to master's wording, byte-exact
full suite green                   1561 passed, 12 skipped
gate clean over the repository     normalise, witness and deleted-symbols all clean

1561 = the 1554 baseline after my #344 merge + exactly the 7 new test functions (test_normalise_handle_gate.py 44 -> 51). I re-ran this on the new master rather than reporting the number I first measured: my own #344 merge moved master under this review, which is the base-drift trap I have walked into before.

Carded, not blocked: tsk-sgia5s

The stray docstring space FIX 4 asked about was not removed — it moved up one line:

MASTER 12d77108   no docstring line starts with a space
#343   c1929472   space was on the closing-quote line
PR350  1b6aaaff   closing quote clean; line 28 is now ' function body.'

Acceptance item 5 as written ("the closing triple-quote has no leading space") is literally satisfied, so this is not a block. It is carded rather than waved through because I have left this kind of residue untracked before and had to re-find it.

Merging. This is a correct fix to a defect I missed, verified across every scope shape rather than the ones the PR names.

— @taOSmd-dev

@jaylfc
jaylfc merged commit 769f98d into master Aug 18, 2026
7 checks passed
jaylfc added a commit that referenced this pull request Aug 18, 2026
…ationships honestly (#348)

Revision of #342, which was blocked because the fabrication had migrated from
numbers to a RELATIONSHIP. That block is resolved: every number and every
enumeration here was re-measured independently at review time.

    CLAIM                                          MEASURED                     VERDICT
    oracle byte size    15388478                   stat -c %s -> 15388478        ok
    oracle sha256       821a2034d219ab45...        sha256sum  -> 821a2034...     ok
    oracle questions    500                        len(json)  -> 500             ok
    evidence-only haystacks, all 500               500 / 500 exact, 0 differing  ok
      control: distinct question_ids               500 (file is not degenerate)
    s_full.json "does not exist on this machine"   find -> 0 hits, whole box     ok
      control: oracle find -> 10 hits, 3 repos, all byte-identical
    clean_longmemeval.py "does not exist"          find -> 0 hits                ok
    longmemeval_granularity.py uses s_cleaned      DATA_PATH line 24             ok
    oracle loader list (was 2 of 3)                grep -> exactly those 3       ok
    97.0% / REPRODUCE-longmemeval.md sentence      restored                      ok

The new 9-runner enumeration replacing the `longmemeval_*` catch-all reconciles
in both directions: 9 of 9 real, 0 invented. The 3 found-but-unclaimed are the
README itself, REPRODUCE-longmemeval.md (a doc, cited separately) and
run_on_fedora.sh (a shell wrapper), none of them runners.

Marking longmemeval_s_cleaned.json as NOT YET PINNED rather than guessing its
size and count is the right call and the opposite of what this chain was
blocked for twice.

Suite 1561 passed / 12 skipped = exactly the post-#350 baseline, which is what a
docs-only change must produce. All three gates clean; no conflict markers.

The automated REVIEW WARNING on this PR is a FALSE POSITIVE, now 4 for 4
(#331, #342, #345, #348). tsk-7cl7rk asks for measurements, never tests, and
this is a README plus a changelog fragment. Do not re-triage it on a docs card.

CARDED, not blocked:

- tsk-r44fqf: acceptance item 3 asked the PR body to state whether the
  longmemeval_s_full.json pin was verified and to file a card if not; neither
  was done. The pin (277383467 bytes, d6f21ea9...) is not known to be wrong, it
  is UNVERIFIABLE on this box, and it sits in the one file whose numbers have
  been wrong three times running, formatted identically to the numbers that are
  checked. Card filed by the reviewer rather than bouncing a correct docs fix.
- tsk-sgia5s: the README lost its trailing newline (0x0a on master, 0x2a here).
  Control: 38 of 40 sampled .md files on master end 0x0a, and the 2 that do not
  are changelog fragments already covered by tsk-v2pr2b.

Note for the record, not actioned here: there is still no gate on this file
(git grep -ln 'longmemeval|data/README' -- scripts .github -> 0 hits), because
tsk-yssnei's doc-gate died with #334. That remains tsk-r4j272 territory, and it
is why all three fabrications in this chain had to be caught by hand.
jaylfc added a commit that referenced this pull request Aug 18, 2026
… a defect survived two reviews because the probes covered only the shapes the change named

Post-clear #32 and #33. Merged #344 (4175edf), #350 (769f98d), #348 (17ea02a).
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