rigortype 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +82 -20
  3. data/data/core_overlay/numeric.rbs +33 -0
  4. data/data/core_overlay/pathname.rbs +25 -0
  5. data/data/core_overlay/string_scanner.rbs +28 -0
  6. data/data/gem_overlay/activesupport/core_ext.rbs +473 -0
  7. data/data/vendored_gem_sigs/ast/ast.rbs +130 -0
  8. data/data/vendored_gem_sigs/bcrypt/bcrypt.rbs +47 -0
  9. data/data/vendored_gem_sigs/bundler/bundler.rbs +238 -0
  10. data/data/vendored_gem_sigs/cgi/cgi_extras.rbs +34 -0
  11. data/data/vendored_gem_sigs/did_you_mean/did_you_mean_extras.rbs +34 -0
  12. data/data/vendored_gem_sigs/idn-ruby/idn.rbs +54 -0
  13. data/data/vendored_gem_sigs/mysql2/client.rbs +55 -0
  14. data/data/vendored_gem_sigs/mysql2/error.rbs +5 -0
  15. data/data/vendored_gem_sigs/mysql2/result.rbs +31 -0
  16. data/data/vendored_gem_sigs/mysql2/statement.rbs +5 -0
  17. data/data/vendored_gem_sigs/nokogiri/nokogiri.rbs +2332 -0
  18. data/data/vendored_gem_sigs/nokogiri/nokogiri_html5.rbs +47 -0
  19. data/data/vendored_gem_sigs/pg/pg.rbs +212 -0
  20. data/data/vendored_gem_sigs/prism/prism_supplement.rbs +44 -0
  21. data/data/vendored_gem_sigs/redis/errors.rbs +50 -0
  22. data/data/vendored_gem_sigs/redis/future.rbs +5 -0
  23. data/data/vendored_gem_sigs/redis/redis.rbs +348 -0
  24. data/data/vendored_gem_sigs/redis/redis_extras.rbs +130 -0
  25. data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +226 -0
  26. data/docs/handbook/01-getting-started.md +311 -0
  27. data/docs/handbook/02-everyday-types.md +337 -0
  28. data/docs/handbook/03-narrowing.md +359 -0
  29. data/docs/handbook/04-tuples-and-shapes.md +321 -0
  30. data/docs/handbook/05-methods-and-blocks.md +339 -0
  31. data/docs/handbook/06-classes.md +305 -0
  32. data/docs/handbook/07-rbs-and-extended.md +427 -0
  33. data/docs/handbook/08-understanding-errors.md +373 -0
  34. data/docs/handbook/09-plugins.md +241 -0
  35. data/docs/handbook/10-sorbet.md +347 -0
  36. data/docs/handbook/11-sig-gen.md +312 -0
  37. data/docs/handbook/12-lightweight-hkt.md +333 -0
  38. data/docs/handbook/README.md +275 -0
  39. data/docs/handbook/appendix-elixir.md +370 -0
  40. data/docs/handbook/appendix-go.md +399 -0
  41. data/docs/handbook/appendix-java-csharp.md +470 -0
  42. data/docs/handbook/appendix-liskov.md +580 -0
  43. data/docs/handbook/appendix-mypy.md +370 -0
  44. data/docs/handbook/appendix-phpstan.md +338 -0
  45. data/docs/handbook/appendix-protocols-and-structural-typing.md +292 -0
  46. data/docs/handbook/appendix-rust.md +446 -0
  47. data/docs/handbook/appendix-steep.md +336 -0
  48. data/docs/handbook/appendix-type-theory.md +1662 -0
  49. data/docs/handbook/appendix-typeprof.md +416 -0
  50. data/docs/handbook/appendix-typescript.md +332 -0
  51. data/docs/install.md +189 -0
  52. data/docs/llms.txt +72 -0
  53. data/docs/manual/01-installation.md +342 -0
  54. data/docs/manual/02-cli-reference.md +557 -0
  55. data/docs/manual/03-configuration.md +152 -0
  56. data/docs/manual/04-diagnostics.md +206 -0
  57. data/docs/manual/05-inspecting-types.md +109 -0
  58. data/docs/manual/06-baseline.md +104 -0
  59. data/docs/manual/07-plugins.md +92 -0
  60. data/docs/manual/08-skills.md +143 -0
  61. data/docs/manual/09-editor-integration.md +245 -0
  62. data/docs/manual/10-mcp-server.md +532 -0
  63. data/docs/manual/11-ci.md +274 -0
  64. data/docs/manual/12-caching.md +116 -0
  65. data/docs/manual/13-troubleshooting.md +120 -0
  66. data/docs/manual/14-rails-quickstart.md +332 -0
  67. data/docs/manual/15-type-protection-coverage.md +204 -0
  68. data/docs/manual/16-rbs-extended-annotations.md +190 -0
  69. data/docs/manual/17-driving-improvement.md +160 -0
  70. data/docs/manual/README.md +87 -0
  71. data/docs/manual/ci-templates/README.md +58 -0
  72. data/docs/manual/plugins/README.md +86 -0
  73. data/docs/manual/plugins/rigor-actioncable.md +78 -0
  74. data/docs/manual/plugins/rigor-actionmailer.md +74 -0
  75. data/docs/manual/plugins/rigor-actionpack.md +80 -0
  76. data/docs/manual/plugins/rigor-activejob.md +58 -0
  77. data/docs/manual/plugins/rigor-activerecord.md +102 -0
  78. data/docs/manual/plugins/rigor-activestorage.md +74 -0
  79. data/docs/manual/plugins/rigor-activesupport-core-ext.md +86 -0
  80. data/docs/manual/plugins/rigor-devise.md +70 -0
  81. data/docs/manual/plugins/rigor-dry-schema.md +56 -0
  82. data/docs/manual/plugins/rigor-dry-struct.md +60 -0
  83. data/docs/manual/plugins/rigor-dry-types.md +59 -0
  84. data/docs/manual/plugins/rigor-dry-validation.md +62 -0
  85. data/docs/manual/plugins/rigor-factorybot.md +76 -0
  86. data/docs/manual/plugins/rigor-graphql.md +89 -0
  87. data/docs/manual/plugins/rigor-hanami.md +83 -0
  88. data/docs/manual/plugins/rigor-mangrove.md +73 -0
  89. data/docs/manual/plugins/rigor-minitest.md +86 -0
  90. data/docs/manual/plugins/rigor-pundit.md +72 -0
  91. data/docs/manual/plugins/rigor-rails-i18n.md +92 -0
  92. data/docs/manual/plugins/rigor-rails-routes.md +94 -0
  93. data/docs/manual/plugins/rigor-rails.md +44 -0
  94. data/docs/manual/plugins/rigor-rbs-inline.md +83 -0
  95. data/docs/manual/plugins/rigor-rspec-rails.md +72 -0
  96. data/docs/manual/plugins/rigor-rspec.md +86 -0
  97. data/docs/manual/plugins/rigor-shoulda-matchers.md +78 -0
  98. data/docs/manual/plugins/rigor-sidekiq.md +78 -0
  99. data/docs/manual/plugins/rigor-sinatra.md +61 -0
  100. data/docs/manual/plugins/rigor-sorbet.md +63 -0
  101. data/docs/manual/plugins/rigor-statesman.md +75 -0
  102. data/docs/manual/plugins/rigor-typescript-utility-types.md +71 -0
  103. data/exe/rigor +1 -1
  104. data/lib/rigor/analysis/incremental_session.rb +4 -2
  105. data/lib/rigor/analysis/run_stats.rb +13 -1
  106. data/lib/rigor/analysis/runner.rb +54 -12
  107. data/lib/rigor/cli/check_command.rb +26 -3
  108. data/lib/rigor/cli/coverage_command.rb +67 -92
  109. data/lib/rigor/cli/coverage_mutation.rb +149 -0
  110. data/lib/rigor/cli/docs_command.rb +248 -0
  111. data/lib/rigor/cli/fused_protection_renderer.rb +67 -0
  112. data/lib/rigor/cli/fused_protection_report.rb +76 -0
  113. data/lib/rigor/cli/skill_command.rb +103 -41
  114. data/lib/rigor/cli/skill_describe.rb +346 -0
  115. data/lib/rigor/cli.rb +25 -3
  116. data/lib/rigor/config_audit.rb +152 -0
  117. data/lib/rigor/configuration.rb +12 -0
  118. data/lib/rigor/environment/rbs_loader.rb +27 -0
  119. data/lib/rigor/environment.rb +49 -1
  120. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +140 -38
  121. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +37 -6
  122. data/lib/rigor/inference/scope_indexer.rb +87 -89
  123. data/lib/rigor/inference/statement_evaluator.rb +27 -0
  124. data/lib/rigor/plugin/isolation.rb +5 -5
  125. data/lib/rigor/plugin/loader.rb +4 -2
  126. data/lib/rigor/protection/diagnostic_oracle.rb +51 -0
  127. data/lib/rigor/protection/mutation_scanner.rb +98 -38
  128. data/lib/rigor/protection/mutator.rb +21 -0
  129. data/lib/rigor/protection/test_suite_oracle.rb +68 -0
  130. data/lib/rigor/signature_path_audit.rb +92 -0
  131. data/lib/rigor/version.rb +1 -1
  132. data/skills/rigor-ask/SKILL.md +172 -0
  133. data/skills/rigor-doctor/SKILL.md +87 -0
  134. data/skills/rigor-editor-setup/SKILL.md +114 -0
  135. data/skills/rigor-mcp-setup/SKILL.md +117 -0
  136. data/skills/rigor-monkeypatch-resolve/SKILL.md +79 -0
  137. data/skills/rigor-next-steps/SKILL.md +113 -0
  138. data/skills/rigor-plugin-tune/SKILL.md +79 -0
  139. data/skills/rigor-protection-uplift/SKILL.md +133 -0
  140. data/skills/rigor-rbs-setup/SKILL.md +128 -0
  141. data/skills/rigor-upgrade/SKILL.md +79 -0
  142. metadata +120 -1
@@ -0,0 +1,336 @@
1
+ # Appendix — Coming from Steep
2
+
3
+ [Steep](https://github.com/soutaro/steep) is the established
4
+ Ruby static type checker, and the de-facto reference
5
+ implementation of RBS-driven analysis. If you have used Steep,
6
+ the most important thing to know is that **Rigor reads the
7
+ same `.rbs` files** — your existing signatures port over
8
+ unchanged. The two tools are complementary, not exclusive.
9
+
10
+ This appendix is for users who already think in Steep
11
+ vocabulary and want to know which Rigor concept matches which
12
+ Steep concept.
13
+
14
+ ## The five-second pitch
15
+
16
+ | Question | Steep | Rigor |
17
+ | --- | --- | --- |
18
+ | Source of types | `.rbs` files (mandatory at boundaries) | `.rbs` files (optional — inference fills gaps) |
19
+ | Annotations in `.rb` | `# @type` comments, type assertions | Almost none — `assert_type` / `dump_type` are introspection helpers |
20
+ | Coverage requirement | Steepfile's `check`/`signature` directives demand annotated targets | None — `rigor check lib` works with zero `.rbs` |
21
+ | Default for unannotated code | Errors when you ask Steep to check it | Inferred precisely or `Dynamic[top]` |
22
+ | Tool focus | Strong typing on opt-in surface | Best-effort precision on every file |
23
+ | Diagnostic philosophy | Surface all type-shape mismatches | Stay silent unless the bug is provable |
24
+
25
+ If Steep's slogan is "Ruby with optional manifest types,"
26
+ Rigor's is "Ruby with proven facts." The two are designed for
27
+ overlapping but distinct workflows.
28
+
29
+ ## Both consume RBS: the common ground
30
+
31
+ This is the headline. RBS is Ruby's standard signature
32
+ language; both Steep and Rigor read it as their canonical
33
+ type source. A `.rbs` file you wrote for Steep works in Rigor
34
+ without changes:
35
+
36
+ ```rbs
37
+ # sig/slug.rbs
38
+ class Slug
39
+ def normalise: (String) -> String
40
+ def self.default_length: () -> Integer
41
+ end
42
+ ```
43
+
44
+ Steep checks the body of `Slug#normalise` against this sig and
45
+ errors when the return type drifts. Rigor checks the same
46
+ thing under the `def.return-type-mismatch` rule (Chapter 8).
47
+ Both tools agree on the contract.
48
+
49
+ The tools diverge on what they layer on top:
50
+
51
+ - **Steep** adds method-body type-checking and a strict
52
+ "every method on the path must have a sig" expectation
53
+ (configurable, but the default).
54
+ - **Rigor** adds inference everywhere (so missing sigs
55
+ produce `Dynamic[top]`, not errors), refinement carriers,
56
+ constant folding, and plugin-side narrowing.
57
+
58
+ ## Type vocabulary — the RBS-level mapping is identity
59
+
60
+ Because both tools speak RBS, the type vocabulary at the
61
+ declaration level is the same:
62
+
63
+ | RBS form | Steep | Rigor |
64
+ | --- | --- | --- |
65
+ | `String` | `String` | `Nominal[String]` (display: `String`) |
66
+ | `Integer?` | `Integer \| nil` | `Integer \| Constant<nil>` (display: `Integer?`) |
67
+ | `Array[Integer]` | `Array[Integer]` | `Array[Integer]` |
68
+ | `[Integer, String]` (tuple) | tuple | `Tuple[Integer, String]` |
69
+ | `{name: String, age: Integer}` (record) | record | `HashShape{name: String, age: Integer}` |
70
+ | `_Comparable` (interface) | structural | structural facet |
71
+ | `untyped` | `untyped` | `Dynamic[top]` (display: `untyped`) |
72
+ | `bot` | `bot` | `Bot` |
73
+ | `top` | `top` | `Top` |
74
+ | `bool` | `bool` | `Constant<true> \| Constant<false>` (display: `bool`) |
75
+ | `void` | `void` | `void` |
76
+
77
+ Rigor's internal type carriers (`Type::Constant`,
78
+ `Type::IntegerRange`, `Type::Refined`, `Type::Tuple`,
79
+ `Type::HashShape`) do NOT exist in Steep's surface. They are
80
+ **erased to the RBS-equivalent** at the boundary, so a method
81
+ declared `-> String` in RBS still satisfies its caller's
82
+ expectation even if Rigor knows the result is
83
+ `non-empty-lowercase-string` internally.
84
+
85
+ This erasure contract is documented at
86
+ [`docs/type-specification/rbs-erasure.md`](../type-specification/rbs-erasure.md).
87
+
88
+ ## Annotations in `.rb` source
89
+
90
+ Steep recognises a small set of in-source type annotations:
91
+
92
+ | Steep `.rb` annotation | Rigor equivalent |
93
+ | --- | --- |
94
+ | `# @type var x: Integer` | (no analogue in core) |
95
+ | `# @type self: Foo` | `T.bind(self, Foo)` via `rigor-sorbet` plugin |
96
+ | `# @type method foo: () -> String` | RBS file declaration |
97
+ | `_ = x` (type cast) | `T.cast(x, T)` via `rigor-sorbet` plugin |
98
+
99
+ Rigor deliberately does NOT ship in-source annotation comments
100
+ in core. The reasoning (ADR-0, ADR-5, robustness principle):
101
+
102
+ 1. **`.rb` files stay clean for runtime developers.** Authors
103
+ who do not care about types do not see type comments.
104
+ 2. **Annotations belong at the boundary.** Rigor's stance is
105
+ that the public contract lives in `.rbs`, not at every
106
+ variable assignment.
107
+ 3. **Inference covers most variables.** When `x = some_call`,
108
+ Rigor knows the return type of `some_call` — there is
109
+ nothing to annotate.
110
+
111
+ When you genuinely want in-source assertions (you are
112
+ migrating from Steep or Sorbet, or you have a complex
113
+ narrowing the engine cannot follow), the `rigor-sorbet`
114
+ plugin is the supported path — see Chapter 10.
115
+
116
+ ## Steepfile vs `.rigor.yml`
117
+
118
+ | Steep `Steepfile` | Rigor `.rigor.yml` |
119
+ | --- | --- |
120
+ | `target :lib do ... end` | `paths: [lib]` |
121
+ | `check "lib"` | covered by `paths:` |
122
+ | `signature "sig"` | `signature_paths: [sig]` (auto-detected when omitted) |
123
+ | `library "set", "json"` | `rbs_collection.lock.yaml` (RBS gem-collection) — same mechanism Steep uses |
124
+ | `configure_code_diagnostics` | `severity_overrides:`, `severity_profile:` |
125
+ | Multiple targets per Steepfile | Multiple `paths:` entries (single profile per project) |
126
+
127
+ The biggest config difference: Steep's per-target structure
128
+ lets you check `lib/` strictly and `app/` permissively in the
129
+ same project. Rigor's profile is project-wide, with per-rule
130
+ and per-file overrides for granularity.
131
+
132
+ ## Severity model
133
+
134
+ Both tools have severity controls; the shapes are slightly
135
+ different.
136
+
137
+ | Steep | Rigor |
138
+ | --- | --- |
139
+ | `configure_code_diagnostics(D::Ruby.strict)` per target | `severity_profile: strict` project-wide |
140
+ | `D::Ruby.lenient` / `default` / `strict` / `all_error` | `lenient` / `balanced` / `strict` |
141
+ | Per-diagnostic severity in Steepfile | `severity_overrides:` in `.rigor.yml` |
142
+ | `D::Ruby::UnknownConstant = :error` | `severity_overrides: { call.undefined-method: error }` |
143
+
144
+ The rule identifiers do not align 1:1 — Steep's are class
145
+ names, Rigor's are dotted families. The conceptual model is
146
+ the same: a default level, plus per-rule promotion / demotion.
147
+
148
+ ## Diagnostic vocabulary
149
+
150
+ Steep's diagnostic catalogue and Rigor's overlap for the same
151
+ underlying conditions, but the names differ.
152
+
153
+ | Steep | Rigor |
154
+ | --- | --- |
155
+ | `Ruby::NoMethod` | `call.undefined-method` |
156
+ | `Ruby::ArgumentTypeMismatch` | `call.argument-type-mismatch` |
157
+ | `Ruby::IncompatibleAssignment` | (covered by `def.ivar-write-mismatch` for instance variables; locals are not flagged) |
158
+ | `Ruby::MethodBodyTypeMismatch` | `def.return-type-mismatch` |
159
+ | `Ruby::UnknownConstant` | (covered by `call.undefined-method` against the receiver class) |
160
+ | `Ruby::UnexpectedKeywordArgument` | `call.argument-type-mismatch` (keyword binding flows through the same rule) |
161
+ | `Ruby::IncompatibleTypeCase` | (no direct analogue today) |
162
+
163
+ A practical implication: a project that runs both Steep and
164
+ Rigor will see overlapping diagnostics on shape errors and
165
+ complementary diagnostics on the things each tool catches that
166
+ the other does not. The
167
+ [`docs/notes/20260503-steep-cross-check-triage.md`](../notes/20260503-steep-cross-check-triage.md)
168
+ note is a worked example — Steep and Rigor were run against
169
+ the same project and the diagnostic streams categorised.
170
+
171
+ ## Suppression
172
+
173
+ | Steep | Rigor |
174
+ | --- | --- |
175
+ | `# steep:ignore` | `# rigor:disable all` |
176
+ | `# steep:ignore Ruby::NoMethod` | `# rigor:disable call.undefined-method` |
177
+ | (no file-scope syntax) | `# rigor:disable-file <rule>` |
178
+ | `Steepfile`: per-target `ignore_paths:` (path-scoped) | `.rigor.yml`: `exclude:` (path-scoped); `disable:` is the rule-scoped axis |
179
+
180
+ Rigor's suppression vocabulary is closer to PHPStan's and
181
+ RuboCop's than to Steep's, but the intent matches.
182
+
183
+ ## "No annotations needed" — the largest practical difference
184
+
185
+ Steep, by default, expects every method on the checked path
186
+ to have an RBS sig (or to opt out via `# @type` annotations).
187
+ Running `steep check` on a project with no `sig/` directory
188
+ produces lots of "missing sig" reports.
189
+
190
+ Rigor, by default, infers what it can and stays silent when it
191
+ cannot. Running `rigor check lib` on a project with no `sig/`
192
+ directory produces a small number of high-confidence
193
+ diagnostics — the methods Rigor was able to prove unsound from
194
+ the body alone.
195
+
196
+ This is by design (ADR-0). The two tools serve different
197
+ adoption stages:
198
+
199
+ - **Greenfield, type-discipline-from-day-one project.** Steep
200
+ is excellent. Write the RBS first; check the body against
201
+ it.
202
+ - **Existing codebase, gradual hardening.** Rigor is excellent.
203
+ Start with zero `.rbs`, get diagnostics on the worst bugs
204
+ immediately, add `.rbs` only where inference cannot see far
205
+ enough.
206
+ - **Both at once.** Run them side by side. They share input
207
+ (the same RBS). Steep's diagnostic stream and Rigor's
208
+ diagnostic stream complement each other.
209
+
210
+ ## What Steep has and Rigor does not
211
+
212
+ - **`@type` comments in source.** Whatever your stance on
213
+ in-source annotations, Steep ships a richer surface for
214
+ them. `# @type var x: Integer`, `# @type self: Foo`, and
215
+ the `_ = x` cast operator have no Rigor-core equivalent.
216
+ The `rigor-sorbet` plugin fills the gap (Chapter 10).
217
+ - **Method-body type checking against declared params.** Steep
218
+ enforces "every reference to parameter `x` inside the body
219
+ agrees with the declared `x: Integer`." Rigor's analogous
220
+ check is `def.return-type-mismatch`; the parameter-side check
221
+ is comparable but more conservative (RBS-erased view).
222
+ - **Tighter generics inference.** Steep's generic instantiation
223
+ in chained calls is more aggressive than Rigor's today.
224
+ - **Diagnostic taxonomy maturity.** Steep's diagnostic
225
+ catalogue has had more years to settle; Rigor's is smaller
226
+ and growing.
227
+
228
+ ## What Rigor has and Steep does not
229
+
230
+ - **Inference without RBS.** A `lib/` directory with zero
231
+ `.rbs` files produces useful Rigor output. Steep needs sigs.
232
+ - **Refinement carriers with automatic narrowing.**
233
+ `non-empty-string` from `unless s.empty?`, `positive-int`
234
+ from `n > 0`, etc.
235
+ - **Constant folding through method calls.** `"foo".upcase`
236
+ resolves to `Constant<"FOO">`, not just `String`. Steep's
237
+ literal types are narrower than Rigor's.
238
+ - **Plugin-side return-type contributions.** Steep does not
239
+ have an equivalent to Rigor's `dynamic_return` —
240
+ if a domain DSL's return type depends on the literal first
241
+ argument, Rigor models it; Steep does not.
242
+ - **Sorbet-input adapter.** A `rigor-sorbet` migration is
243
+ zero-cost for projects mid-Sorbet (`sig { ... }` blocks and
244
+ RBI files become inputs to Rigor's catalog). Steep does not
245
+ read Sorbet sigs.
246
+ - **Cache-driven incremental analysis.** Rigor's per-file
247
+ cache survives across runs and across machine boundaries
248
+ (ADR-6). Steep's incremental story is improving but not
249
+ yet at parity.
250
+
251
+ ## A coexistence pattern
252
+
253
+ A common, low-friction setup for a project that wants both
254
+ checkers:
255
+
256
+ ```yaml
257
+ # .rigor.yml
258
+ paths: [lib]
259
+ severity_profile: balanced
260
+ # signature_paths is auto-detected; sig/ is shared with Steep
261
+ ```
262
+
263
+ ```ruby
264
+ # Steepfile
265
+ target :lib do
266
+ check "lib"
267
+ signature "sig"
268
+ configure_code_diagnostics D::Ruby.default
269
+ end
270
+ ```
271
+
272
+ Both tools read the same `sig/`. CI runs `steep check` and
273
+ `rigor check lib` as separate steps. Each tool's
274
+ output goes to its own annotation channel. When they disagree
275
+ on the same line, the standing rule is: **if Steep flags it
276
+ and Rigor does not, investigate**. Steep tends to surface sig
277
+ drift that Rigor's RBS-erasure consciously absorbs; Rigor
278
+ tends to surface body-level facts that Steep does not check.
279
+
280
+ ## A migration vignette
281
+
282
+ Suppose you maintain a project that has been on Steep for two
283
+ years. The `sig/` tree is comprehensive; `# @type` annotations
284
+ appear in a handful of files where inference fell short. You
285
+ want to add Rigor without uprooting anything.
286
+
287
+ Steps:
288
+
289
+ 1. **Add Rigor as a dev dependency.** No changes to `sig/`.
290
+ 2. **Run `rigor check lib` once.** You will see a
291
+ small number of new diagnostics — typically narrowing-aware
292
+ findings Steep does not produce (`flow.always-truthy-condition`,
293
+ `def.return-type-mismatch` against an `RBS::Extended`-tightened
294
+ return). Triage as bugs vs noise.
295
+ 3. **Decide what to do with `# @type` annotations.** Rigor
296
+ ignores them (they are comments to the parser). Two
297
+ options:
298
+ a. Leave them — Steep keeps using them, Rigor ignores
299
+ them. No-op coexistence.
300
+ b. Convert to `T.let` / `T.cast` from the `rigor-sorbet`
301
+ plugin if you want Rigor to honour the assertion as
302
+ well.
303
+ 4. **Add Rigor to CI.** Both checkers run; both gates must
304
+ pass before merge.
305
+ 5. **Optionally tighten existing sigs with `RBS::Extended`.**
306
+ Steep treats `%a{rigor:v1:...}` as ordinary RBS comments;
307
+ Rigor treats them as refinement directives. The same
308
+ `.rbs` file produces stricter Rigor output and unchanged
309
+ Steep output.
310
+
311
+ The migration is low-friction because the
312
+ foundational assumption (RBS as the contract language) is
313
+ shared.
314
+
315
+ ## What's next
316
+
317
+ You probably do not need to read the rest of this appendix
318
+ section sequentially. Three useful pointers:
319
+
320
+ - [Chapter 7 — RBS and `RBS::Extended`](07-rbs-and-extended.md)
321
+ if you want to see how the directive grammar layers on top
322
+ of the RBS you already write.
323
+ - [Chapter 8 — Understanding errors](08-understanding-errors.md)
324
+ for the rule catalogue, severity profiles, and baseline
325
+ diffing — the analogue to Steep's diagnostic config.
326
+ - [`docs/notes/20260503-steep-cross-check-triage.md`](../notes/20260503-steep-cross-check-triage.md)
327
+ for a worked side-by-side run of Steep and Rigor on the
328
+ same project (the project itself).
329
+
330
+ If you want to compare against another tool, the sibling
331
+ appendix pages cover [TypeProf](appendix-typeprof.md) — Ruby's
332
+ inference-first tool, the closest cousin to Rigor's own
333
+ `sig-gen` — plus [TypeScript](appendix-typescript.md),
334
+ [PHPStan](appendix-phpstan.md), [mypy](appendix-mypy.md),
335
+ [Java / C#](appendix-java-csharp.md), [Rust](appendix-rust.md),
336
+ [Go](appendix-go.md), and [Elixir](appendix-elixir.md).