Skip to content

feat(metrics): count a plain re-assignment in python/embedded_python/ruby state_mutation (#2817) - #2964

Merged
squid-protocol merged 2 commits into
mainfrom
fix/2817-state-mutation-plain-reassign
Sep 11, 2026
Merged

feat(metrics): count a plain re-assignment in python/embedded_python/ruby state_mutation (#2817)#2964
squid-protocol merged 2 commits into
mainfrom
fix/2817-state-mutation-plain-reassign

Conversation

@squid-protocol

Copy link
Copy Markdown
Owner

Summary

state_mutation (feeds risk_state_flux) counted only self.x =/@ivar =, :=, global/nonlocal, container mutators and (ruby) bang methods for python/embedded_python/ruby — never a plain x = v. That is narrower than the #2765 contract's corollary 1 (a language with no declaration syntax counts the assignment statement as the write), so count = count + 1 scored 1 in lua/php/shell/go and 0 in python — the cross-language inequality #2812 removes. The contract doc recorded these three as deferred.

Fix: one house-style assignment arm (mirroring lua/javascript) added to each rule, plus re.M (inert for the existing arms). Anchored on the black/PEP8 convention that a statement assignment writes with a space before = (x = 1) while a keyword argument does not (x=1):

(?:^|;)[ \t]*<lvalue>(?:\.attr|[idx])*[ \t]+=(?![=])(?![^\n(]{0,300},[ \t]*$)
  • Counts x = v, obj.attr = v, d[k] = v.
  • Excludes foo(x=1), def f(x=1) (no space before =, not statement-start), x == 1, and annotated x: int = 1 (declaration-with-initializer).
  • ruby uses a lowercase-initial lvalue (so CONST = 1 stays freeze_hits) and drops =~/=>.
  • Out of scope (documented): compound x += 1 and unspaced x=1.

Crucible incidence table (language-crucible @ v1.2.0)

language before after new-arm hits multi-line-call FP candidates true false positives
python 1490 11647 10988 6 (0.05%) ~1–3 (last spaced kwarg with no magic trailing comma in a non-black multi-line call; 2 were in a .rst doc file)
embedded_python 191 894 838 0 0
ruby 64 140 76 0 0

The space-before-= anchor + the trailing-comma guard reduce kwargs/multi-line-call false positives to ~0.02%. Sampled matches are otherwise all genuine writes.

Golden rebless (this PR)

Both masters regenerated per-leg in isolated venvs (full-precision / zero-dep). bless_scope.py on both legs: ~4133 diffs = 2656 corpus-wide topological (X/Y/Z) re-solve + ~1478 substantive, all attributable to the reprice — Variable Reassignments/State Flux Exposure/Amplified Cascading Flux directly, plus normalized exposures / Structural Magnitude / directory-group aggregates that are relative to a file's total magnitude. Every raw count change is on a .py/.rb file (incl. python files nested in mixed-language projects like cpp/NVDA); no unchanged-rule language moves its own raw count, and no files were newly parsed or excluded. The scoring layer absorbs the raw increase gracefully (e.g. braille.py reassignments 617→1801 but State Flux Exposure only 55.3→58.3).

Verification

  • Rule/contract/strict suites: 372 passed.
  • tests/extraction/ + tests/core_engine/: 8337 passed, 0 failed.
  • tests/signal_contract_audit.py --ci: no new findings (contract stays stated; only counts move).
  • tests/ruff_audit.py --ci (pinned ruff 0.16.0): no new lint findings; the 3 engine files are format-clean.

Docs

docs/state_mutation_rule_contract.md: python/ruby/embedded_python rows moved deferred → agrees with before → after counts; the "what the contract cannot do with a regex" note rewritten to "landed in #2817".

Label: rosetta:rebless-owed — the keyword-rosetta corpus re-bless PR opens against engine main after merge.

Closes #2817

🤖 Generated with Claude Code

squid-protocol and others added 2 commits September 11, 2026 13:05
…ruby state_mutation

The state_mutation rule (feeds risk_state_flux) counted only self.x=/@ivar=,
:=, global/nonlocal, container mutators and (ruby) bang methods for these three
languages -- never a plain `x = v`. That is narrower than the #2765 contract's
corollary 1 (where a language has no declaration syntax, the assignment
statement is the write), so `count = count + 1` scored 1 in lua/php/shell/go and
0 in python -- the cross-language inequality #2812 exists to remove.

Add one house-style assignment arm (mirroring lua/js) to each rule, plus re.M
(inert for the existing arms, which use no ^/$). The arm is anchored on the
black/PEP8 convention that a statement assignment writes with a space before `=`
(`x = 1`) while a keyword argument does not (`x=1`):

  (?:^|;)[ \t]*<lvalue>(?:\.attr|[idx])*[ \t]+=(?![=])(?!...,$)

so `x = v`, `obj.attr = v` and `d[k] = v` count, while `foo(x=1)`, `def f(x=1)`,
`x == 1` and annotated `x: int = 1` (declaration-with-initializer) do not. ruby
uses a lowercase-initial lvalue (so `CONST = 1` stays freeze_hits) and drops
`=~`/`=>`. Compound `+=` and unspaced `x=1` are deliberately out of scope.

Tests: extend the cross-language state_mutation contract (CASES + exact-count
COUNTS incl. multi-line-call cases), add an embedded_python entry, and flip
ruby's `x = 1` from a negative to a positive (it enshrined the narrow behavior);
refresh the three per-language strict rows. docs/state_mutation_rule_contract.md:
move python/ruby/embedded_python from "deferred" to "agrees" and rewrite the
narrowness note. Corpus reprice (raw findall @ language-crucible v1.2.0): python
1490->11647, embedded_python 191->894, ruby 64->140; kwargs/multi-line-call
false-positive rate ~0.02%.

Golden-master rebless owed (scoring-layer change).

Closes #2817

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014rUcYDAzYYCgrLdHyKBT9G
The #2817 plain-assignment arm raises the state_mutation raw count for
python/embedded_python/ruby (corpus: python 1490->11647, embedded_python
191->894, ruby 64->140), which reprices risk_state_flux population-wide.
Both golden masters regenerated per-leg in isolated venvs (full-precision:
all deps; zero-dep: PyYAML only) against language-crucible @ v1.2.0.

bless_scope.py (both legs): ~4133 diffs = 2656 corpus-wide topological
(X/Y/Z) re-solve + ~1478 substantive, all attributable to the reprice:
"Variable Reassignments"/"State Flux Exposure"/"Amplified Cascading Flux"
directly, and normalized exposures (Error/Cognitive/Concurrency), Structural
Magnitude and directory-group aggregates that are relative to a file's total
magnitude. Every raw "Variable Reassignments" count change is on a .py/.rb
file (incl. python files nested in mixed-language project dirs like cpp/NVDA);
no language whose rule was not changed has its own raw count move, and NO
files were newly parsed or excluded. The scoring layer absorbs the raw
increase gracefully (e.g. braille.py reassignments 617->1801 but State Flux
Exposure only 55.3->58.3).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014rUcYDAzYYCgrLdHyKBT9G
@squid-protocol squid-protocol added the rosetta:rebless-owed Intentionally moves keyword-rosetta counts; audit warns, corpus re-blesses after merge label Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🐦‍⬛ Muninn Security Scan

✅ No security issues found.

🐦‍⬛ Powered by Muninn · Skald Lab

@squid-protocol
squid-protocol marked this pull request as ready for review September 11, 2026 17:34
@squid-protocol
squid-protocol merged commit 9941162 into main Sep 11, 2026
41 of 44 checks passed
@squid-protocol
squid-protocol deleted the fix/2817-state-mutation-plain-reassign branch September 11, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rosetta:rebless-owed Intentionally moves keyword-rosetta counts; audit warns, corpus re-blesses after merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

state_mutation: python, embedded_python and ruby never count a plain re-assignment (narrower than the #2765 contract)

1 participant