SlopScore
00 crowd

audition (github.com/yaroslav/audition)

Auditions Ruby scripts, gems, Rack apps, and Rails applications for the ability to run under Ractors: static analysis of Ractor-isolation violations plus dynamic in-Ractor probing, with explanations and fixes.
Ruby · ★ 26 · 1 forks · MIT · paperwork by the Cap'mmostly ai (inferred)light human (inferred)works-on-my-machine (inferred)other
listed 2 hours ago by yaroslav · last checked 2 hours ago
The owner didn't write this. This repo never submitted itself. The Cap'm found it on a truffle trawl and wrote its paperwork from what GitHub already shows. Picked by hand by the Cap'm on 2026-09-11: A Ruby tool that audits scripts, gems and Rails apps for Ractor compatibility; its README warns "The entire codebase was written by Claude Fable 5 (Anthropic).". 26 stars; MIT license. The owner did not submit this. Votes count; awards don't until the owner claims it.

I'm not calling your project slop! Geeze, it's a joke... Do you own this repo?

Log in with GitHub as yaroslav. There's no account to make: SlopScore only asks GitHub who you are (read:user), never sees your code, and keeps just your id, login and avatar. Then you can:

  • Keep it, on your terms. Commit your own slopscore.md (spec) and press Refresh. Your paperwork replaces the Cap'm's, and you can submit it for Slop of the Day.
  • Take it down. One click on Remove. It stays gone; the trawl never brings it back.

Log in with GitHub

Can't log in as the owner? Request a takedown. No login needed, and a trawled listing comes down right away.

GitHub says
Auditions Ruby scripts, gems, Rack apps, and Rails applications for the ability to run under Ractors: static analysis of Ractor-isolation violations plus dynamic in-Ractor probing, with explanations and fixes.
topics
analysisdynamic-analysislinterparallelismractorractorsrailsrubystatic-analysis
created
2026-07-17 · pushed 6 days ago · 57 commits · 2 contributors
release
v0.3.0 · 2026-09-05
languages
Ruby 100%
paperwork
licensereadme 42% health
dependencies
no dependency graph (no manifest, or disabled) · OSV.dev, checked 2 hours ago

Disclosures, inferred by the Cap'm

slopbucket
vibe-coded
category
other
ai_generated
mostly
human_touch
light
status
works-on-my-machine
language (detected)
ruby
topic (detected)
analysisdynamic-analysislinterparallelismractorractorsrailsrubystatic-analysis
license (detected)
mit

The Cap'm's log

The Cap'm wrote this paperwork, not the owner. This repo never submitted itself to SlopScore. The Cap'm picked it by hand: A Ruby tool that audits scripts, gems and Rails apps for Ractor compatibility; its README warns "The entire codebase was written by Claude Fable 5 (Anthropic).". It carries the MIT license. The disclosures above are his best guess from what GitHub shows.

Is this yours? Commit a real slopscore.md and press Refresh to replace this, or remove the listing in one click. There's no account to make: you log in with GitHub.

README (read the rest on GitHub)

Audition

Point it at a Ruby script, a gem, a Rack app, or a Rails root and it tells you whether that code can run inside Ractors, why it cannot, and how to fix it. Unlike a linter, audition does not stop at pattern-matching your source: whole-program analysis is powered by rubydex, Shopify's Ruby indexer, and the target is also loaded in a sandboxed subprocess to observe real Ractor::IsolationErrors on the live object graph. Some of the checks and fixes were trained on how Rails core itself is being ractorized; see the pattern study.

gem audition

GitHub Release Docs

  • Three probes, one verdict. Per-file Prism AST checks, whole-program semantic analysis powered by rubydex (Shopify's code graph; class-level state is resolved across files and reopenings), and dynamic in-Ractor execution of the actual target.
  • Explains, not just flags. Every finding carries a why (which rule of the Ractor model it violates) and a fix (what to write instead).
  • --fix like RuboCop, in two tiers. Safe corrections: .freeze on string constants, sentinels, and containers whose elements are all shareable, Ractor.make_shareable(...) for the remaining mutable and shallow-frozen containers and for Proc constants, and boot-time hoisting of method-body requires. --fix-unsafe adds semantics-affecting rewrites: magic-comment insertion, freeze-on-memoize for class-level memoization (both @x ||= and return @x if defined?(@x) idioms keep their caching, the memoized value becomes shareable, Rails-core style; Ractor.store_if_absent only for block initializers and invalidated caches), autoload to require, and write-once globals/class variables to frozen constants. --dry-run previews everything as a diff.
  • Dependency-aware. Runtime findings are attributed to their source via const_source_location; when your own code is clean but a dependency is not, the verdict is a distinct blocked state, so globalid is not blamed for ActiveSupport's state.
  • Dogfooding. The scanner for Ractor compatibility is built using Ractors: static analysis fans out across CPU cores on Ractor workers, and audition passes its own audit. It runs on itself on every commit (a lefthook pre-commit over the staged files) and on every push (a full self-audit in CI).
  • Trained on Rails core. Several checks and fix suggestions come straight from studying the Rails ractorization effort (some two hundred commits and pull requests): Hash.new default procs, in-place mutation of registry constants, closure-carrying define_method, copy-on-write rewrites. The findings are documented in docs/rails_core_best_practices.md.
  • Terminal-native output. Colors, glyphs, and OSC 8 hyperlinks; path:line is clickable in supporting terminals. JSON output for CI.
$ audition worker.rb
* audition 0.1.0 ruby 4.0.6 · script at .

  worker.rb
    x raises inside a Ractor: Ractor::IsolationError: can not
      access global variable $jobs from non-main Ractor
      why: The script ran fine on the main Ractor but failed under
      Ractor.new; the static findings usually pinpoint the line.
    x worker.rb:1  write to global variable $jobs
      why: Non-main Ractors cannot access global variables; this
      raises Ractor::IsolationError the moment the line executes
      in a Ractor (verified on Ruby 4.0).
      fix: Pass the value into the Ractor explicitly
      (Ractor.new(value) { |v| ... }) or over a Ractor::Port; for
      per-Ractor state use Ractor.current[:key].
    x worker.rb:4  read of global variable $jobs
      ...

  dynamic probes
    x script probe failed (details above)

  summary: 3 errors
  verdict: x not ractor-ready
$ echo $?
1

And the whole-bundle view:

$ audition Gemfile.lock --static-only
╭───────────────┬─────────┬───────────┬────────┬──────────┬─────────╮
│ gem           │ version │ verdict   │ errors │ warnings │ fixable │
├───────────────┼─────────┼───────────┼────────┼──────────┼─────────┤
│ activesupport │ 8.1.0   │ not ready │    157 │       97 │      77 │
│ i18n          │ 1.14.7  │ not ready │     48 │       40 │      45 │
│ mail          │ 2.9.1   │ not ready │     27 │        4 │      13 │
│ rack          │ 3.2.6   │ not ready │     23 │       45 │      60 │
│ ...           │         │           │        │          │         │
╰───────────────┴─────────┴───────────┴────────┴──────────┴─────────╯
0 of 11 gems ractor-ready

Requires Ruby 4.0 or newer, strictly: the tool targets the modern Ractor API (Ractor::Port, Ractor#value, main-Ractor require proxying) and its verified semantics.

Warning

The entire codebase was written by Claude Fable 5 (Anthropic). It has a thorough spec suite and was validated against real gems, but no human has reviewed every line. Be wary; read before you trust, especially --fix rewrites.

Table of contents

Installation

gem install audition

Or in a Gemfile:

gem "audition", require: false

Usage

audition worker.rb            # a script: static + run inside Ractor
audition my_gem               # an installed gem, by name
audition path/to/gem-checkout # a gem working copy (*.gemspec)
audition path/to/rack-app     # a config.ru directory
audition path/to/rails-root   # a Rails application
audition lib                  # any directory, static-only
audition a.rb b.rb c.rb       # several files at once, statically
audition Gemfile.lock         # sweep every gem in the bundle
audition path/to/app --deps   # same, from the app root

Useful flags:

Flag Effect
--deps sweep the target's Gemfile.lock gem by gem
--write-baseline / --no-baseline record / ignore known findings
--fix apply safe corrections, then re-check
--fix-unsafe also apply semantics-affecting corrections
--dry-run with a fix flag: preview edits, change nothing
--format json machine-readable report for CI
--format github GitHub Actions annotations + job summary
--compare old.json delta vs a previous report: fixed/introduced
--static-only / --dynamic-only pick one probe layer
--fail-on warning stricter CI gate (default: error)
--exit-zero report findings but never fail the build
--capabilities table of what this Ruby allows in Ractors
--timeout 60 dynamic probe budget in seconds
--plain no colors or hyperlinks (also via NO_COLOR, pipes)

Exit codes: 0 clean, 1 findings at or above the --fail-on threshold (or a failed dynamic probe), 2 usage error. --exit-zero (alias for --fail-on never) always exits 0 unless the invocation itself is broken.

Adopting incrementally

Nobody goes from 150 findings to zero in one commit. Three tools keep the gate useful from day one:

Baseline. Record today's findings, then fail CI only on new ones:

audition . --write-baseline    # writes .audition-baseline.json
audition .                     # exit 0; summary shows "N baselined"

The ledger stores per-check-per-file counts, so line drift never invalidates it. --no-baseline shows everything again.

Inline pragmas. Silence a single line, rubocop-style:

$legacy_flag = true # audition:disable global-variables
risky_call          # audition:disable

Project config. .audition.yml at the target root (CLI flags always win):

fail_on: warning
timeout: 60
exclude:
  - legacy/**
  - db/schema.rb
checks:
  disable:
    - at-exit

CI and git hooks

GitHub Actions. --format github turns findings into workflow-command annotations that land right on the PR diff, and appends a verdict-plus-counts markdown table to the job summary page. A blocking gate:

- uses: ruby/setup-ruby@v1
  with:
    ruby-version: "4.0"
- run: gem install audition
- run: audition --format github .

To surface findings without failing the build while you adopt (the flag other linters call --exit-zero too, so it keeps its name here):

- run: audition --format github --exit-zero .

--fail-on never is the long form, and works from .audition.yml as well; GitHub's own continue-on-error: true on the step is the workflow-level equivalent.

Git hooks. Passing several .rb files audits exactly those files statically, which is the shape hook managers hand over. With lefthook:

pre-commit:
  commands:
    audition:
      glob: "*.rb"
      run: audition --static-only --plain {staged_files}

With pre-commit:

- repo: local
  hooks:
    - id: audition
      name: audition
      language: system
      entry: audition --static-only --plain
      types: [ruby]

Config, pragmas, and the baseline resolve against the working directory, so a hook run from the repository root honors the same .audition.yml as a full audit.

This repository eats its own dog food. Every commit runs audition --static-only over the staged files through lefthook (next to standardrb), and every push and pull request gets a full self-audit in CI with annotations and a job summary, non-blocking via --exit-zero (audit.yml). Current state: own code audits ready; the full dynamic probe reports the terminal dev-dependencies as blocked, which is exactly the distinction the verdict system exists to make.

What it catches

Static, with file:line precision:

  • Global variables, with a verified allowlist: $stdout, $~, $!, $VERBOSE writes and friends stay legal.
  • Class variables, resolved on the rubydex graph.
  • Class-level instance variables, unified across the class body, def self., and class << self, across files; the classic @cache ||= {} and return @x if defined?(@x) memoizations.
  • Constants that are not deeply shareable: bare mutable literals, interpolated strings, the subtle shallow freeze ([[1], [2]].freeze still raises; audition explains why), and call results the magic comment never covers (X.tr(":", ""), Regexp.new, Regexp.union, format), the shapes Rails fixed last in its own ractorization. Honors # frozen_string_literal: and # shareable_constant_value: magic comments.
  • Sync primitives and Procs in constants (Mutex, Queue, lambdas), including Hash.new { } default procs, which stay unshareable even after .freeze.
  • Registry-style constant mutation (RENDERERS << key, LOOKUP[k] = v) and define_method with a literal block (the method carries an unshareable Proc); both patterns and their fixes come from the Rails core ractorization study.
  • Runtime require and autoload (serializes all Ractors through the main-Ractor proxy).
  • Ractor.new blocks capturing outer locals (the ArgumentError at creation time), resolved through Prism's exact scope depths.
  • Hostile or removed APIs: Ractor.yield/take (gone in 4.0), ActiveSupport cattr_*/mattr_* class variables (with the class_attribute migration Rails itself made) and class_attribute without copy-on-write writes, include Singleton, fork, ObjectSpace._id2ref, ENV mutation.
  • Native extensions that never declare Ractor safety: a byte scan of every compiled .bundle/.so for the rb_ext_ractor_safe import, which also covers precompiled platform gems that ship no sources; an unbuilt checkout is scanned at the source level (ext/**, C, Rust, or Zig) instead. A silent extension raises Ractor::UnsafeError on every call from a non-main Ractor, so it rates a warning; a declared one gets an info note, because the declaration is the maintainer's assertion, not a proof.

Dynamic, on the live object graph:

  • Runs scripts inside a real Ractor (via load, which is not proxied) and reports the actual exception.
  • Requires a library, then sweeps every constant it introduced with Ractor.shareable?, and inspects every class and module for class-level ivars and class variables, with const_source_location attribution.
  • Records every compiled extension the load pulled in, dependencies included, and byte-scans each for the rb_ext_ractor_safe import; silent ones are reported against the dependency that ships them. Ruby's own extensions are left to Ruby.
  • Boots config.ru and serves one GET / entirely inside a Ractor, the per-worker model of Ractor web servers; then hammers it from 4 Ractors x 25 requests to surface failures that only appear under concurrency.
  • Boots Rails (config/environment.rb), eager-loads, and sweeps the application's namespaces.

Agent skill

This repository ships a ractor-readiness skill that teaches coding agents (Claude Code and friends) the full audition workflow: audit, fix tiers, suite-parity verification, and incremental adoption. It lives in skills/ractor-readiness/SKILL.md.

Install into Claude Code as a plugin:

/plugin marketplace add yaroslav/audition
/plugin install audition@audition

Or install the skill with the skills CLI:

$ npx skills add yaroslav/audition

Extending

Checks are written in a small declarative DSL and can be registered from outside the gem:

class NoSleep < Audition::Static::Checks::Base
  check_name "no-sleep"

  explain :sleepy,
          severity: :warning,
          message: "sleep inside potential Ractor code",
          why: "Blocking one Ractor blocks its whole OS thread.",
          fix: "Prefer Ractor::Port#receive with a timeout."

  on :call_node do |node|
    flag(node, :sleepy) if node.name == :sleep && !node.receiver
  end
end

Audition::Static::Checks.register(NoSleep)

on generates the Prism visitor and always continues traversal; explain entries are a message catalog with %{placeholders}.

Development

bundle install
bundle exec rake spec       # RSpec suite
bundle exec rake standard   # standardrb lint
lefthook install            # pre-commit standardrb + audition
bundle exec exe/audition --capabilities

Static scanning is Ractor-parallel on large targets (one worker per core, minus one for the main Ractor); audition's own lib/ passes audition lib clean.

The design notes in docs/design.md include the empirically verified Ruby 4.0 Ractor semantics table that the checks are calibrated against.

Acknowledgements

The whole-program checks stand on rubydex, Shopify's high-performance static analysis suite for Ruby: audition feeds every file into its graph and reads state ownership back out. Thanks to its authors, in particular the top five contributors: Alexandre Terrasa, Vinicius Stock, Alex Rocha, Stan Lo, and Soutaro Matsumoto.

Assisted by

Claude Fable 5.

License

MIT. See LICENSE.txt.

Scan report · 2026-09-11
  • Prohibited terms or links
  • Repository eligibility
  • slopscore.md paperwork
  • Content policy
  • Risk review

0 comments

log in to comment.

report this listinglog in to report