check_rename refuses a Ruby predicate/bang name it indexes happily: new_name must be a plain identifier rejects foo? #295

Closed
opened 2026-09-22 20:33:11 +02:00 by buildagent · 1 comment
Member

Reported from a live Ruby project, reproduced here.

The measurement

module Pred
  def self.issued_for_us?(t) = !t.nil?
  def self.check(t) = issued_for_us?(t)
end

The index stores the symbol with the ?:

search_symbols("issued_for_us") -> id=20  name="issued_for_us?"

But renaming it to another perfectly ordinary Ruby predicate name is refused:

check_rename {"symbol_id":20,"new_name":"mcp_client_token?"}
-> {"error":"invalid_new_name","hint":"new_name must be a plain identifier"}

Why it matters

? and ! suffixes are not exotic Ruby — they are the convention for predicates and for mutating/raising variants, and they appear on a large share of methods in any idiomatic Ruby codebase. The tool indexes those names correctly and then refuses to reason about renaming to one.

The reporter also observed the second-order effect: with a plain name substituted, check_rename found both edit sites, but its text scan could not run on the ? name, so the safety net that compensates for unresolved references was unavailable on exactly the methods where Ruby resolution is weakest (see #294). It reported that honestly rather than claiming a clean rename, which is the disclosure working — but the answer is still degraded for a name the language considers ordinary.

Scope to establish before fixing

The validator should accept what each language accepts, not one lowest common denominator:

  • Ruby: trailing ? and !; also = for setters (name=), and operator method names (<=>, [], []=, +).
  • Other six: confirm nothing currently relies on the stricter rule. C#/TS generic suffixes and PHP's $ are the obvious places to check.

Whatever shape the fix takes, the text scan must run on the accepted names too — a validator that admits foo? while the scan silently skips it would move the blindness rather than remove it.

Suggested acceptance

  1. check_rename accepts a name valid in the symbol's own language, and refuses with a message naming the language and what it allows.
  2. The text scan covers the accepted set, with the regex/escaping for ?, ! and = measured rather than assumed.
  3. A fixture per language with at least one name the old validator rejected, and a mutation that reverts the validator and goes red per language.

Found alongside #294 in the same Ruby dogfood. Lower severity than that one: this refuses loudly, where #294 answers confidently and wrongly.

Reported from a live Ruby project, reproduced here. ## The measurement ```ruby module Pred def self.issued_for_us?(t) = !t.nil? def self.check(t) = issued_for_us?(t) end ``` The index stores the symbol with the `?`: ``` search_symbols("issued_for_us") -> id=20 name="issued_for_us?" ``` But renaming it to another perfectly ordinary Ruby predicate name is refused: ``` check_rename {"symbol_id":20,"new_name":"mcp_client_token?"} -> {"error":"invalid_new_name","hint":"new_name must be a plain identifier"} ``` ## Why it matters `?` and `!` suffixes are not exotic Ruby — they are the convention for predicates and for mutating/raising variants, and they appear on a large share of methods in any idiomatic Ruby codebase. The tool indexes those names correctly and then refuses to reason about renaming to one. The reporter also observed the second-order effect: with a plain name substituted, `check_rename` found both edit sites, **but its text scan could not run on the `?` name**, so the safety net that compensates for unresolved references was unavailable on exactly the methods where Ruby resolution is weakest (see #294). It reported that honestly rather than claiming a clean rename, which is the disclosure working — but the answer is still degraded for a name the language considers ordinary. ## Scope to establish before fixing The validator should accept what each language accepts, not one lowest common denominator: * **Ruby**: trailing `?` and `!`; also `=` for setters (`name=`), and operator method names (`<=>`, `[]`, `[]=`, `+`). * **Other six**: confirm nothing currently relies on the stricter rule. C#/TS generic suffixes and PHP's `$` are the obvious places to check. Whatever shape the fix takes, the **text scan must run on the accepted names too** — a validator that admits `foo?` while the scan silently skips it would move the blindness rather than remove it. ## Suggested acceptance 1. `check_rename` accepts a name valid in the symbol's own language, and refuses with a message naming the language and what it allows. 2. The text scan covers the accepted set, with the regex/escaping for `?`, `!` and `=` measured rather than assumed. 3. A fixture per language with at least one name the old validator rejected, and a mutation that reverts the validator and goes red per language. Found alongside #294 in the same Ruby dogfood. Lower severity than that one: this refuses loudly, where #294 answers confidently and wrongly.
buildagent referenced this issue from a commit 2026-09-23 20:23:14 +02:00
Author
Member

Fixed in v0.31.2 (PR #299, commit 85bb486).

lang_profile::IDENTIFIER_SUFFIXES grants Ruby one trailing ?, ! or =, read through profile_name so the packaged de.h-dv.ruby/ruby id gets the same answer (the #112 trap). check_rename checks shape first and then the symbol's own language; text_occurrences no longer goes dark on those names, since ?, ! and = are literal inside an FTS5 phrase.

On the reported case, issued_for_us? → mcp_client_token? now finds both edit sites with every evidence channel clear, the text channel included. A Rust symbol still refuses valid?, with a message naming its language. Three mutations were run, each red on its named test.

Not changed: renaming to a Ruby operator method (<=>, []=) is still refused. That was out of scope and remains a loud refusal, not a silent one.

Fixed in **v0.31.2** (PR #299, commit `85bb486`). `lang_profile::IDENTIFIER_SUFFIXES` grants Ruby one trailing `?`, `!` or `=`, read through `profile_name` so the packaged `de.h-dv.ruby/ruby` id gets the same answer (the #112 trap). `check_rename` checks shape first and then the symbol's own language; `text_occurrences` no longer goes dark on those names, since `?`, `!` and `=` are literal inside an FTS5 phrase. On the reported case, `issued_for_us?` → `mcp_client_token?` now finds both edit sites with every evidence channel `clear`, the text channel included. A Rust symbol still refuses `valid?`, with a message naming its language. Three mutations were run, each red on its named test. Not changed: renaming to a Ruby operator method (`<=>`, `[]=`) is still refused. That was out of scope and remains a loud refusal, not a silent one.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
h-dv/code-index#295
No description provided.