Rigor
Type-aware bug finding for Ruby — zero annotations, and a zero-false-positive bar enforced against real codebases. Run one command over the code you already have, and trust every line of output.
gem install rigortype && rigor check app lib
(The tool itself runs on Ruby 4.0; your project can stay on whatever Ruby it targets — see Get started.)
No type annotations to write or maintain, no runtime dependency, no
changes to your code. Rigor parses Ruby with
Prism and runs a flow-sensitive
inference engine that reasons about the values your expressions
produce — not just their classes. It catches undefined methods (and
typos) on inferred receivers, wrong argument counts, receivers that
can be nil on a live path, unreachable branches and case clauses,
and conditions that can only ever be truthy.
Hello, Rigor
A realistic typo, caught with the receiver's computed value in the message:
# demo.rb
def slug(title)
title.downcase.gsub(/\s+/, "-")
end
s = slug("Hello World")
s.lenght
$ rigor check demo.rb
demo.rb:7:3: error: undefined method `lenght' for "hello-world"
Note what the error says: not undefined method `lenght' for String
but for "hello-world" — Rigor traced the value through the
method call and the downcase.gsub chain, with no annotations
anywhere.
To see what Rigor sees, run rigor annotate fact.rb — it reprints
the file with the inferred type of every line in the margin. The
#=> comments below are added by annotate, not part of the
source:
# fact.rb
def factorial(n) #=> Integer
(1..n).reduce(1, :*) #=> Integer
end #=> :factorial
answer = factorial(5) #=> 120
No signature on factorial, yet the method types as Integer — and
at the call site, where the argument is known, Rigor folds the whole
computation down to the value 120. The same machinery tracks hash
shapes through your params, string values through builder chains, and
nil through guard clauses in everyday application code. (Output is
syntax-highlighted — through bat
when installed.)
Get started in one prompt
The fastest path is to hand your AI coding agent (Claude Code, Cursor, or any assistant that follows instructions from a URL) this prompt:
Install Rigor in this project by following the instructions at
https://raw.githubusercontent.com/rigortype/rigor/refs/heads/master/docs/install.md
It installs Rigor, then runs the bundled rigor-project-init
Agent Skill: it walks your Gemfile,
proposes plugins matched to your stack (Rails, Sinatra, dry-rb, …),
lets you pick an adoption mode — baseline (acknowledge existing
diagnostics, work them down incrementally) or strict
(zero-diagnostic gate from day one) — and commits a ready-to-use
configuration.
This works in your language. The prompt is plain natural language, so you can write it — and run the whole setup conversation — in your mother tongue. In Japanese, for example:
次の手順に従って、このプロジェクトに Rigor をインストールしてください:
https://raw.githubusercontent.com/rigortype/rigor/refs/heads/master/docs/install.md
Ready-made prompts — 日本語, 简体中文, 繁體中文, 한국어, Español, Português, Français, Deutsch, Italiano, Tiếng Việt, ภาษาไทย, Bahasa Indonesia, Polski, Українська, Русский, Română, Türkçe, العربية, فارسی — are in the installation guide.
Manual install — Rigor is a tool, not a library: install it
independently, not in your project's Gemfile. It runs on Ruby
4.0 regardless of which Ruby your project targets
(mise provisions both, pinned per project):
mise use ruby@4.0
mise use gem:rigortype # or: gem install rigortype
The gem is named rigortype (the name rigor was taken on RubyGems);
the executable is rigor. asdf, dev containers, and CI templates are
in the installation guide
and CI guide.
Daily commands
rigor check lib # find bugs (caches under .rigor/ — gitignore it)
rigor init # drop a starter .rigor.yml
rigor annotate lib/foo.rb # reprint a file with inferred types in the margin
rigor triage lib # summarise diagnostics: distribution, hotspots
rigor sig-gen --diff lib/foo.rb # emit RBS from inference (--write to save)
In CI, rigor check auto-detects the platform and emits native output
(GitHub annotations, GitLab Code Quality, SARIF, Checkstyle, JUnit,
TeamCity).
Three design commitments
- Types are facts, not wishes. Hand-written annotations drift the
moment they are written. Rigor infers from the code itself — every
type is derived from what your source actually produces. When you do
want RBS in
sig/,rigor sig-genemits it from inference results so the written form starts in sync with reality. - A false positive is the worst bug. Your program works; a static analyzer that argues otherwise is the thing that is wrong. Rigor fires a diagnostic only when the receiver type is statically known and the evidence is conclusive — never on a value it merely failed to prove things about — and every precision change is gated on real OSS codebases (Mastodon, Redmine, GitLab FOSS) before it ships. You should never have to write defensive code, or a suppression comment, to calm the analyzer down.
- Programmable inference beyond unions. A plain union
(
Integer | nil) is not the type story Ruby needs. Rigor tracks literal values, integer ranges, refinements likenon-empty-string, and per-position tuple / hash shapes — and exposes a plugin API plus a macro / DSL expansion substrate so Rails-shape DSLs become first-class type sources.
Works with your stack
Production plugins ship in-repo for the common frameworks and gems —
Rails (ActiveRecord, ActionPack, routes, i18n, jobs, mailers),
testing (RSpec, FactoryBot, minitest), ecosystem (Sidekiq,
Devise, Pundit, Sinatra, dry-rb, GraphQL), and type-system bridges
(rigor-sorbet ingests Sorbet sig / RBI files, so Rigor runs
alongside an existing Sorbet setup). If you already maintain RBS in
sig/ — from Steep or by hand — Rigor reads it as a type source
out of the box. Activate plugins in .rigor.yml:
paths: [lib, app]
plugins:
- rigor-activerecord
- rigor-actionpack
- rigor-rspec
The full catalogue is in plugins/README.md; the
rigor-project-init Skill picks the right set for you. To teach Rigor
your own DSL, the rigor-plugin-author
Skill authors a plugin step-by-step, and
rigor-baseline-reduce drives the
baseline down once you are running.
Going deeper
Where a vanilla checker answers "what class is this?", Rigor answers
"what subset of values can this expression produce?" — literal
values (Constant<"hello">), integer ranges (positive-int),
refinements (non-empty-string = String - ""), tuple and known-key
hash shapes. Every narrower carrier erases to its base class for RBS
interop, so adopting Rigor is strictly additive. When you want to
declare more than RBS can say, optional %a{rigor:v1:…} annotations
in sig/ files tighten types while staying a no-op for ordinary RBS
tools.
The twelve-chapter handbook walks the whole type model; the type specification and user manual are the reference companions.
Status
Current release: v0.1.18 (2026-06-11), with v0.2.0 — the first
evaluation release — landing shortly. Rigor analyses real Ruby today:
the 0.1.x line has been hardened against Mastodon, Redmine, and
GitLab FOSS, and the deliberately conservative rule catalogue grows
only as fast as the zero-false-positive bar allows. Release history:
CHANGELOG.md · forward-looking commitments:
Roadmap.
Contributing
See CONTRIBUTING.md for the git clone →
green-tests path and a map of the spec / ADR / skill documentation
contributors should read.
License
Mozilla Public License Version 2.0. See LICENSE.