Skip to content

Enable Style/FrozenStringLiteralComment - #24

Merged
dduugg merged 1 commit into
mainfrom
enable-frozen-string-literal-comment
Sep 26, 2026
Merged

dduugg merged 1 commit into
mainfrom
enable-frozen-string-literal-comment

Conversation

@dduugg

@dduugg dduugg commented Sep 26, 2026

Copy link
Copy Markdown
Contributor

Why

# frozen_string_literal: true makes plain string literals immutable. Accidental in-place mutation of a literal then fails loudly with a FrozenError instead of silently changing shared state, and repeated literals share one object instead of allocating a new one on each evaluation. Every Ruby file in this repo already carries the pragma, but Style/FrozenStringLiteralComment was disabled, so nothing kept new files from leaving it out.

Config change

.rubocop.yml: removed

Style/FrozenStringLiteralComment:
  Enabled: false

With that removed, the cop uses its defaults (Enabled: true, EnforcedStyle: always). No Exclude was added for the cop. The existing AllCops Exclude (bin/**/*, vendor/**/*) is unchanged, and the bin/ Ruby binstubs already have the pragma anyway.

Files that got the pragma

0. All 37 files rubocop inspects (lib/, spec/, exe/chatwerk, chatwerk.gemspec, Gemfile, Rakefile, config.ru) already had # frozen_string_literal: true on main, so the whole diff is the .rubocop.yml deletion. There are no exclusions. In lib/chatwerk.rb and lib/chatwerk/api.rb the pragma sits on line 2, after # typed: strict. The cop and Sorbet/ValidSigil both accept that order, and I checked that Ruby 3.3, 3.4 and 4.0 still honor the pragma there.

Runtime fixes

None needed. Every file already had the pragma, so runtime behavior on this branch is identical to main. I still audited the code for literal mutation, and every mutation site is safe:

  • lib/chatwerk/api.rb:15,17 (Api.print_env) and lib/chatwerk/tools/print_env_tool.rb:24,26 (PrintEnvTool.call) do msg << .... msg is an interpolated <<~MESSAGE heredoc, and interpolated strings are not frozen on Ruby >= 3.0 (the gemspec requires >= 3.3). Api.print_env has no spec coverage; I exercised it by hand (see below).
  • lib/chatwerk/views/base_view.rb:32-33 (BaseView#say) appends to a +'' buffer. The message = '' default and "\n" are only read.
  • Frozen literals passed to dependencies are never mutated by them: tool descriptions and input_schema hashes, Response text, and the server name go to mcp 1.4.0; desc strings and map %w[...] go to thor 1.5.0; metadata values and IO.popen argv go to RubyGems.

Verification

  • Specs: main (9a9cfc4) had 58 examples and 0 failures on Ruby 3.4.2. This branch has 58 examples and 0 failures on Ruby 3.3.11, 3.4.2 and 4.0.2, matching the CI matrix. It also passes with RUBYOPT=--enable-frozen-string-literal and with -W:deprecated, with no "literal string will be frozen" warnings.

  • rubocop: 37 files inspected, no offenses. I confirmed the cop is now active: a file without the pragma is flagged.

  • Static sweep: I read every Ruby file in lib/, exe/, bin/, spec/, the gemspec, Gemfile, Rakefile and config.ru, looking for mutating String calls (<<, concat, bang methods, force_encoding, buffer arguments, mutated default arguments and constants). I also traced literals returned from methods and passed into mcp, thor and query_packwerk.

  • Dynamic checks: I used a small packwerk fixture and an empty directory, and ran:

    • exe/chatwerk with version, help, print_env, packages, package, package_todos and package_violations, covering hit, filtered, no-match, multiple-match and missing cases.
    • Chatwerk::Mcp.server.handle_json with initialize, tools/list, and tools/call on all 5 tools.
    • The paths specs don't cover: Api.print_env, the exact-match branch of find_package, and the BaseView fallbacks.

    No FrozenError came up on Ruby 3.3, 3.4 or 4.0.

  • Fresh Eyes: local pre-push review against origin/main found 0 blockers, 0 major, 0 minor and 0 info findings.

Pre-existing issues found (not addressed here, unrelated to frozen strings)

  • chatwerk mcp raises LoadError for mcp/transports/stdio, because mcp 1.4.0 moved it to mcp/server/transports/stdio_transport (lib/chatwerk/cli.rb:11-13).
  • config.ru references Chatwerk::MCPServer, which doesn't exist.
  • lib/chatwerk/views/package_view.rb:9: consumers.uniq.sort! throws away its result.
  • ViolationsDetailsView returns only the say output (the "No sources/files found" lines) when any say fires, which drops the grouped file listing.
  • Helpers.find_package's "Found multiple packages" message is lost when the Api methods rewrap it in a second Chatwerk::Error.

Remove the `Style/FrozenStringLiteralComment: Enabled: false` entry from
.rubocop.yml so the cop runs with its default EnforcedStyle (always).

Every file RuboCop inspects already starts with
`# frozen_string_literal: true`, so no pragmas needed adding and no
mutation fixes were required. The cop now keeps new files from dropping
the pragma.

The string-mutation sites that remain were reviewed and are safe:
- `msg <<` in Api.print_env and PrintEnvTool.call appends to interpolated
  heredocs, which are not frozen on Ruby >= 3.0 (the gem requires >= 3.3).
- BaseView#say appends to an explicit `+''` buffer.
@dduugg
dduugg requested a review from a team as a code owner September 26, 2026 16:43
@dduugg
dduugg merged commit 5028a3f into main Sep 26, 2026
6 checks passed
@dduugg
dduugg deleted the enable-frozen-string-literal-comment branch September 26, 2026 18:37
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