fix(embedded_python): state_mutation hardware arm counted getter reads as writes - #2972
Merged
Merged
Conversation
…reads as writes The hardware-toggle arm `\.(?:value|on|off|high|low|toggle)\s*\(` matched on call syntax alone, so it counted reads as mutations -- a violation of the state_mutation contract (#2765, docs/state_mutation_rule_contract.md): corollary 3 (a read is not a write) and corollary 4 (a token another rule owns is not a second signal). In the meow_turtle corpus this fired on `if recovery_pin.value() == 0:` (a pure getter read) and double-counted `val = self.pin.value()` (the assignment arm already counts the write to `val`; the `.value()` getter added a spurious second hit). `.value(` is BOTH getter and setter in MicroPython/CircuitPython, so it now requires a non-empty argument: `pin.value(1)` writes, `pin.value()` reads. `.on/.off/.high/.low/.toggle` are the no-arg imperative write forms and now match the no-arg shape only, so a parameterised `.on(evt, cb)` -- an event subscription `events`/`listeners` own -- no longer reads as a mutation. Every real write in the corpus is preserved (all `.on()/.off()/ .toggle()` there are no-arg; the five `.value(arg)` setters still count); only the two getter reads drop. The change only tightens quantifiers, so it does not add ReDoS surface. Single-language regex fix under the existing (stated) #2765 contract, not a contract change. Golden masters regenerated (both legs): the diff is confined to embedded_python/meow_turtle state_mutation cells and their rollups, with no file crossing the density aperture. rosetta stays 46/46 (its embedded_python fixture uses none of these tokens) -- no paired corpus rebless owed. New negative cases added to test_state_mutation_contract_2765 and the contract's audit row updated (894 -> 892). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PXAjGXaZKr2Ge6jjZshPRD
Contributor
squid-protocol
marked this pull request as ready for review
September 11, 2026 20:25
squid-protocol
deleted the
fix/embedded-python-state-mutation-getter
branch
September 11, 2026 20:26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
embedded_python'sstate_mutationhardware-toggle arm\.(?:value|on|off|high|low|toggle)\s*\(matched on call syntax alone, so it counted reads as writes — a violation of thestate_mutationcontract (#2765,docs/state_mutation_rule_contract.md):if recovery_pin.value() == 0:(a getter) counted as a mutation.val = self.pin.value()double-counted — the assignment arm already counts the write toval, and the.value()getter added a spurious second hit; and a parameterised.on(evt, cb)is an event subscription thatevents/listenersown.This is not a contract change —
state_mutationis alreadystated(#2765). It's a single-language regex fix under that contract (per theharden-language-extractionskill, which explicitly excludesrule-contract-auditfor single-language regex work), where a corpus measurement showing the rule firing on a read "is a bug in the rule, not a quirk of the corpus."Fix
.value(is both getter and setter in MicroPython/CircuitPython, so it now requires a non-empty argument (pin.value(1)writes,pin.value()reads)..on/.off/.high/.low/.toggleare the no-arg imperative write forms and now match the no-arg shape only, so.on(evt, cb)no longer reads as a mutation. Only tightens quantifiers → no ReDoS surface added.Verification (investigated against the real
meow_turtlecorpus, 14 files).on()/.off()/.toggle()are no-arg (kept); the 5.value(arg)setters still count. Only the 2 getter reads drop (raw 894 → 892).test_state_mutation_contract_2765.py: added getter/subscribe negatives + one-statement-one-hit counts (val = self.pin.value()→ 1,if pin.value() == 0:→ 0,led.on()→ 1). 102 pass.bless_scope= 15 substantive + 5 topological diffs, allembedded_python/meow_turtle(mutation signal, its weighted/cascading views, downstream mass/exposure rollups), no file newly parsed or excluded.ruff_audit/mypy_audit/dead_key_audit--ci: no new findings. Fullpytest tests/: 8746 passed (only the pre-existingtest_security_auditor.pyxgboost-env failures, identical onorigin/main).Contract audit-table row for
embedded_pythonupdated (191 -> 894 -> 892) with the corollary 3/4 rationale.🤖 Generated with Claude Code