Skip to content

Gate and fix data-flow name binding and change detection - #6

Merged
Raman369AI merged 2 commits into
mainfrom
semantic-gate-dataflow-fixes
Sep 27, 2026
Merged

Raman369AI merged 2 commits into
mainfrom
semantic-gate-dataflow-fixes

Conversation

@Raman369AI

Copy link
Copy Markdown
Owner

Summary

This adds a regression gate for method data flow first, then fixes what it catches. The gate follows review feedback: put the failing fixture cases into semantic_cases.json before any more UI work.

Regression gate

tests/semantic_cases.json could only check call resolution. test_semantic_accuracy.py now also runs two data-flow check kinds:

  • "kind": "changes": mustChange / mustNotChange names for a method.
  • "kind": "name": the node kind a name read must have (module_global, closure_variable, exception, builtin, import_reference, external). It also has an allowedKinds form (e.g. comprehension variables, which may leave no outside node) and a mustBeUnbound form (e.g. a read after its except clause).

Nine new cases, each paired with a contrasting check so a fix can't pass by over-correcting:

Case Contrast
Read-only callee doesn't change its argument an appending callee still does
self.items.append changes self.items a method that only reads it changes nothing
Factory -> Repo / -> Optional[Repo] types the receiver an unannotated factory stays unresolved
Module constants, including inside try, are module globals an undefined name stays external
except … as exc binds exc reading exc after the try is unbound
Comprehension variables, including tuple targets, stay local reading one after the comprehension is unbound
Builtins as values (key=str) a module's own len wins
Closures: nested functions, lambdas, and a class inside a function class-body names aren't visible to methods; an enclosing name beats a module global
Imports inside try/if, and __file__ none needed

The new cases fail on main and pass here. The gate has 54 checks, with zero incorrect supported results.

Fixes (dataflow.py, analyzer.py)

  • Name lookup now follows Python's order (local → enclosing function → module → builtin). Module-level names, including those inside top-level blocks, and implicit module attributes resolve as module globals, with the value when assigned once. Names from an enclosing function are closure variables. Builtins are labelled as such. Imports inside if/try/with blocks are indexed.
  • except … as, comprehensions and lambdas bind their names only within their own scope.
  • Change detection:
    • Callees that only call read-only methods (count, index, get, keys, startswith, …) no longer mark the caller's argument as possibly changed. Known mutators and unknown methods still do.
    • self.items.append(item) records a change to self.items, and the summary chip shows self.items and highlights the lines that change it.
  • Factory return annotations type the receiver of a call's result (probably, the same certainty as constructor-based candidates).

Effect on a real codebase (agent-kanban-pm, 1,174 functions, main → this branch)

  • "Unbound read" gaps (unknown names in method data flow): 763 → 0. Threadline's own source is also at 0.
  • False possible changes from read-only callees: 450 → 417.
  • Call resolution: one call newly resolved through a factory annotation; nothing else moves.
  • No data-flow errors before or after; run time is about the same (15 s → 16 s).

Testing

  • python test_semantic_accuracy.py: 54 checks pass, 0 false supported.
  • python -m pytest: 162 pass.
  • tests/browser_smoke.py in all four modes (live, --changes, --html, --html --changes): all pass.
  • mypy (CI scope): passes.

Not in this PR

  • The effect catalog (DB write/read, network, file write, logging, propagated through resolved calls) is planned next, on top of this gate.
  • Delegating resolution to pyright or Jedi remains a separate experiment, and no dependency is added.

🤖 Generated with Claude Code

Raman369AI and others added 2 commits September 26, 2026 21:51
Regression gate: semantic_cases.json gains `changes` and `name` data-flow
checks (with allowedKinds and mustBeUnbound forms). Nine new cases, each
with a contrasting check, fail on main and pass here.

Fixes:
- Module-level names resolve as module globals with their value, including
  inside top-level if/try blocks, plus implicit module attributes.
- Names a nested function or lambda reads from its enclosing function are
  closure variables (LEGB order; class bodies are not enclosing).
- `except ... as exc` is bound in its handler and unbound afterwards.
- Comprehension and lambda variables stay inside their expression; tuple
  comprehension targets are bound.
- Builtins used as values are labelled builtin.
- Imports inside if/try/with blocks are indexed.
- A callee calling only read-only methods on a parameter no longer marks
  the caller's argument as possibly changed.
- self.items.append(item) records a change to self.items; the summary
  shows self/cls changes with their attribute.
- A factory's return annotation (-> Repo, -> Optional[Repo]) types the
  receiver of its result.

On agent-kanban-pm, unknown-name gaps in method data flow fall from 763
to 0 with no new data-flow errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a method from search or a start page rendered it twice: startReview
selected it, then building the call map selected the entry stage, which is
the same method, and redrew the view. The second render briefly showed
"Loading code…" and refetched source and data flow. Native Safari read the
view during that window, failing method_review_default in CI.

chooseScope now skips the redraw when the same snapshot and method are
already rendered. A browser smoke check counts renders when opening a
method; it fails on the old code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Raman369AI
Raman369AI merged commit fa9c7d3 into main Sep 27, 2026
14 checks passed
@Raman369AI
Raman369AI deleted the semantic-gate-dataflow-fixes branch September 27, 2026 03:51
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