Skip to content

feat: detect move contract deltas - #2071

Closed
vitali87 wants to merge 1 commit into
pr-split/split-pr1549-source/pr-1from
pr-split/split-pr1549-source/pr-4
Closed

vitali87 wants to merge 1 commit into
pr-split/split-pr1549-source/pr-1from
pr-split/split-pr1549-source/pr-4

Conversation

@vitali87

Copy link
Copy Markdown
Owner

Teach structural-delta postcondition checks to parse variadic Python headers robustly for move/signature validation.

Files changed

  • codebase_rag/structural_delta.py
  • codebase_rag/tests/test_variadic_header_detection.py

Diff stats

+112 additions, -6 deletions (118 LOC)

Dependencies

This PR depends on: pr-1

Dependency graph

Merge in this order:

pr-1: feat: add move public API and constants
├── pr-2: feat: parse move spans and imports
│   └── pr-5: feat: retarget move imports safely
│       └── pr-6: feat: implement move refactoring operation
│           ├── pr-7: feat: expose move through CLI
│           │   └── pr-11: docs: document move operation
│           ├── pr-8: feat: expose move through MCP
│           │   ├── pr-10: test: cover move MCP wiring
│           │   └── pr-11: docs: document move operation
│           └── pr-9: test: cover move operation behavior
│               └── pr-10: test: cover move MCP wiring
├── pr-3: feat: expose import graph sites for move
│   └── pr-5: feat: retarget move imports safely
│       └── pr-6: feat: implement move refactoring operation
│           ├── pr-7: feat: expose move through CLI
│           │   └── pr-11: docs: document move operation
│           ├── pr-8: feat: expose move through MCP
│           │   ├── pr-10: test: cover move MCP wiring
│           │   └── pr-11: docs: document move operation
│           └── pr-9: test: cover move operation behavior
│               └── pr-10: test: cover move MCP wiring
└── pr-4: feat: detect move contract deltas  <-- this PR
    └── pr-5: feat: retarget move imports safely
        └── pr-6: feat: implement move refactoring operation
            ├── pr-7: feat: expose move through CLI
            │   └── pr-11: docs: document move operation
            ├── pr-8: feat: expose move through MCP
            │   ├── pr-10: test: cover move MCP wiring
            │   └── pr-11: docs: document move operation
            └── pr-9: test: cover move operation behavior
                └── pr-10: test: cover move MCP wiring

@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^feat/.*
  • ^fix/.*

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: vitali87/code-graph-rag/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 9ac8c277-fc6e-4cf8-9874-811122dc6acc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@greptile-apps

greptile-apps Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 2/5

Not safe to merge: reproduced signature-detection failures can either omit real positional-arity regressions or incorrectly reject valid variadic calls.

Findings

  1. P1 Keyword-only suppresses arity errors ▶
  2. P1 Nested defaults truncate headers ▶
  3. P1 One-line suites fail parsing ▶

Summary

This change moves variadic-header detection to Python AST parsing and adds focused coverage, but three reproduced signature cases are still handled incorrectly. Keyword-only parameters suppress real positional-overflow findings, multiline headers can be truncated inside default expressions, and one-line function suites fail parsing. These failures can hide real contract changes or reject correct edits, so the implementation should not merge until they are fixed.

Reviews (1) · Last reviewed commit: "feat: detect move contract deltas"

node = tree.body[0] if tree.body else None
if not isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
return False
return node.args.vararg is not None or bool(node.args.kwonlyargs)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Keyword-only suppresses arity errors

A bare * makes following parameters keyword-only; it does not accept additional positional arguments. For def helper(a, *, b=1), a call with two positional arguments raises TypeError, but this condition classifies the function as variadic and causes the structural arity check to return ok. Real excess-positional contract changes are therefore omitted.

Suggested change
return node.args.vararg is not None or bool(node.args.kwonlyargs)
return node.args.vararg is not None
Artifacts

Evidence from the check

  • Authored and executed script loads the target source functions and compares ordinary versus keyword-only positional arity behavior, confirming the targeted path.

Command output from the check

  • Executed ordinary-signature baseline reports `_header_is_variadic=False`, accepts two positional arguments at runtime, and produces `too_many` from the focused graph check due to the intentionally one-item stored positional fixture.

Command output from the check

  • Executed keyword-only case shows Python raises a two-positional-argument TypeError while `_header_is_variadic=True` makes `_arity_verdict` return `ok`, confirming the defect.

View artifacts

T-Rex Ran code and verified through T-Rex

Comment on lines 611 to 612
if ")" in line:
break

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Nested defaults truncate headers

This stops reading at any line containing ), including parentheses inside a default expression. A valid multiline definition such as def f(\n a=g(),\n *rest,\n): is truncated before *rest, so it is treated as non-variadic. Calls that the function accepts can then be reported as arity regressions, causing correct edits to fail their postcondition.

Artifacts

Evidence from the check

  • Authored isolated Python script that extracts and runs the current repository variadic-detection implementation against a temporary realistic source file, confirming the false negative.

Command output from the check

  • First executed reproduction output shows the current implementation returns false while AST and runtime establish that the function is variadic.

Command output from the check

  • Repeated executed reproduction against the same structural_delta.py hash produces the same false negative, confirming the result is reproducible.

Command output from the check

  • Executed git diff and status check shows no modification to codebase_rag/structural_delta.py during validation.

View artifacts

T-Rex Ran code and verified through T-Rex

if not body:
return False
try:
tree = ast.parse(body + "\n pass\n")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 One-line suites fail parsing

The helper always appends an indented pass, but a one-line definition already has a complete suite. For def f(a, *rest): return a, the constructed source has an unexpected indent, the parse failure returns False, and the variadic function is treated as having a positional overflow. This can reject correct edits through a false arity delta.

Artifacts

Evidence from the check

  • Authored executable extracts and runs the current helper definitions, then reconstructs a one-line variadic source file; it directly exercises the reported path.

Command output from the check

  • Python parsed a header-only `def f(a, *rest):` with an indented pass and reported a variadic argument; the control confirms the intended header-only construction.

Command output from the check

  • The executed current helper code raised the expected parse error for the appended pass and returned false from both direct and reconstructed variadic checks; the finding is confirmed.

View artifacts

T-Rex Ran code and verified through T-Rex

@greptile-apps

greptile-apps Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

Comments Outside Diff

These findings sit on lines the diff does not cover, so they could not be posted inline. Each one leaves this list once its file changes.

  • P1 Keyword-only parameters suppress positional-overflow findings ▶

    • Bug
      • For def helper(a, *, b=1), Python rejects helper(10, 20) with TypeError: helper() takes 1 positional argument but 2 were given. The structural-delta arity path instead reports (1, 'ok'), so it omits the expected DELTA_ARITY_TOO_MANY finding.
    • Cause
      • _header_is_variadic returns node.args.vararg is not None or bool(node.args.kwonlyargs). A bare * creates keyword-only parameters but does not permit additional positional arguments. _arity_verdict treats that true result as permission to suppress a positional-overflow verdict.
    • Fix
      • Make _header_is_variadic return true only when node.args.vararg is not None for this positional-overflow suppressor. Do not treat kwonlyargs as variadic.
  • P1 Multiline variadic function with a call default is treated as non-variadic ▶

    • Bug
      • For a temporary valid source file containing def f( a=g(), *rest, ):, _is_variadic returned False. The same complete header parsed by _header_is_variadic returned True, Python AST reports a vararg, and calling f(1, 2, 3) succeeds with (2, 3).
    • Cause
      • The loop at structural_delta.py:609-612 terminates on the first line containing ). In this source the first match is a=g(),, which is inside a default expression rather than the function signature's closing delimiter. The accumulated text is therefore an incomplete header and _header_is_variadic safely returns False.
    • Fix
      • Accumulate through the actual function-header closing delimiter rather than stopping at any line containing ), for example by parsing progressively until a complete function definition header is available or by using token-aware delimiter tracking that handles nested parentheses and strings.
  • P1 One-line variadic definitions are misclassified as non-variadic ▶

    • Bug
      • _header_is_variadic('def f(a, *rest): return a') returns False, and _is_variadic returns False when it reconstructs that exact one-line source from a definition file. Thus a valid variadic callee can fail to suppress the too-many-positionals arity verdict.
    • Cause
      • The implementation unconditionally calls ast.parse(body + ' pass '). This is valid for a header ending at :, but a one-line suite is already complete; the appended indented statement is an unexpected indent. The caught SyntaxError then produces False.
    • Fix
      • Parse a syntactically complete source form without unconditionally appending an indented suite: append pass only when the normalized input is a header-only definition, or parse the existing complete one-line definition directly.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Unresolved moderate findings affect positional-overflow handling, multiline headers, inline definitions, and the test oracle.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 Medium severity

Open (2)
What changed in this PR

This PR replaces regex-based variadic-header detection with AST parsing for structural-delta arity validation and adds regression tests.

Changes:

  • Adds AST-based header parsing.
  • Covers defaults, nested delimiters, Unicode identifiers, and malformed headers.
  • Adds variadic-header regression tests.
File Summary and findings
codebase_rag/​structural_delta.py Implements AST-based detection. 3 moderate findings: keyword-only arguments incorrectly absorb positional overflow (3 votes); multiline headers can truncate at the first ) (3 votes); complete one-line definitions can fail due to an appended body (1 vote).
codebase_rag/​tests/​test_variadic_header_detection.py Adds parsing tests. 1 moderate finding: the oracle incorrectly treats keyword-only arguments as variadic (1 vote).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

node = tree.body[0] if tree.body else None
if not isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef):
return False
return node.args.vararg is not None or bool(node.args.kwonlyargs)
if open_at < 0 or close_at < 0:
return False
return _VARIADIC.search(text[open_at:close_at]) is not None
return _header_is_variadic("\n".join(header))

vitali87 commented Oct 9, 2026

Copy link
Copy Markdown
Owner Author

Closing as superseded: this is part of the split of #1549, which was closed without merging, and it targets the stale feat/change-signature branch rather than main. The change_signature base was rebuilt on main as #1912 (now stacked as #2161–#2169). The branch is kept, so the move-op work can be rebased onto main if it's picked up again.


Generated by Claude Code

@vitali87 vitali87 closed this Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants