rigortype 0.2.8 → 0.3.0
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/data/core_overlay/csv.rbs +28 -0
- data/data/core_overlay/psych.rbs +22 -0
- data/docs/handbook/01-getting-started.md +9 -1
- data/docs/handbook/02-everyday-types.md +4 -1
- data/docs/handbook/03-narrowing.md +2 -1
- data/docs/handbook/04-tuples-and-shapes.md +7 -3
- data/docs/handbook/06-classes.md +18 -9
- data/docs/handbook/08-understanding-errors.md +4 -3
- data/docs/handbook/11-sig-gen.md +11 -1
- data/docs/handbook/appendix-mypy.md +4 -3
- data/docs/install.md +14 -6
- data/docs/manual/01-installation.md +99 -4
- data/docs/manual/02-cli-reference.md +65 -19
- data/docs/manual/03-configuration.md +34 -2
- data/docs/manual/04-diagnostics.md +23 -0
- data/docs/manual/07-plugins.md +5 -5
- data/docs/manual/09-editor-integration.md +4 -4
- data/docs/manual/11-ci.md +51 -0
- data/docs/manual/12-caching.md +65 -0
- data/docs/manual/14-rails-quickstart.md +4 -2
- data/docs/manual/15-type-protection-coverage.md +21 -0
- data/docs/manual/plugins/rigor-actionpack.md +1 -1
- data/docs/manual/plugins/rigor-activerecord.md +12 -5
- data/docs/manual/plugins/rigor-rails-routes.md +11 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +9 -1
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +5 -3
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +128 -0
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +60 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +3 -2
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +117 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +125 -0
- data/lib/rigor/analysis/check_rules/rule_walk.rb +2 -1
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +2 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +283 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +10 -1
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +93 -0
- data/lib/rigor/analysis/check_rules.rb +557 -95
- data/lib/rigor/analysis/dependency_recorder.rb +93 -9
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -1
- data/lib/rigor/analysis/incremental_session.rb +456 -51
- data/lib/rigor/analysis/path_expansion.rb +42 -0
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +188 -0
- data/lib/rigor/analysis/rule_catalog.rb +226 -1
- data/lib/rigor/analysis/run_cache_key.rb +68 -0
- data/lib/rigor/analysis/run_cache_probe.rb +72 -0
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +181 -28
- data/lib/rigor/analysis/runner/pool_coordinator.rb +77 -15
- data/lib/rigor/analysis/runner/project_pre_passes.rb +95 -49
- data/lib/rigor/analysis/runner/run_snapshots.rb +9 -2
- data/lib/rigor/analysis/runner.rb +343 -68
- data/lib/rigor/analysis/severity_stamp.rb +43 -0
- data/lib/rigor/analysis/worker_session.rb +30 -2
- data/lib/rigor/bleeding_edge.rb +37 -8
- data/lib/rigor/builtins/regex_refinement.rb +36 -1
- data/lib/rigor/cache/descriptor.rb +88 -28
- data/lib/rigor/cache/file_digest.rb +158 -0
- data/lib/rigor/cache/incremental_snapshot.rb +41 -5
- data/lib/rigor/cache/rbs_descriptor.rb +56 -10
- data/lib/rigor/cache/store.rb +207 -60
- data/lib/rigor/ci_detector.rb +90 -0
- data/lib/rigor/cli/annotate_command.rb +15 -9
- data/lib/rigor/cli/baseline_command.rb +4 -1
- data/lib/rigor/cli/check_command.rb +212 -27
- data/lib/rigor/cli/coverage_command.rb +56 -30
- data/lib/rigor/cli/diagnostic_formats.rb +4 -1
- data/lib/rigor/cli/docs_command.rb +0 -30
- data/lib/rigor/cli/doctor_command.rb +163 -23
- data/lib/rigor/cli/lsp_command.rb +5 -0
- data/lib/rigor/cli/mcp_command.rb +5 -0
- data/lib/rigor/cli/plugins_command.rb +36 -6
- data/lib/rigor/cli/plugins_renderer.rb +44 -12
- data/lib/rigor/cli/probe_environment.rb +85 -0
- data/lib/rigor/cli/protection_fork_scan.rb +55 -0
- data/lib/rigor/cli/protection_report.rb +7 -1
- data/lib/rigor/cli/sig_gen_command.rb +36 -9
- data/lib/rigor/cli/skill_command.rb +1 -32
- data/lib/rigor/cli/trace_command.rb +5 -6
- data/lib/rigor/cli/type_of_command.rb +9 -8
- data/lib/rigor/cli/type_scan_command.rb +8 -8
- data/lib/rigor/cli.rb +15 -4
- data/lib/rigor/config_audit.rb +48 -2
- data/lib/rigor/configuration/severity_profile.rb +31 -0
- data/lib/rigor/configuration.rb +156 -8
- data/lib/rigor/environment/default_libraries.rb +35 -0
- data/lib/rigor/environment/missing_gem_constant_index.rb +128 -0
- data/lib/rigor/environment/rbs_loader.rb +164 -5
- data/lib/rigor/environment.rb +71 -43
- data/lib/rigor/inference/budget_trace.rb +77 -2
- data/lib/rigor/inference/closure_escape_analyzer.rb +14 -1
- data/lib/rigor/inference/def_handle.rb +23 -0
- data/lib/rigor/inference/def_node_resolver.rb +90 -0
- data/lib/rigor/inference/def_return_typer.rb +2 -1
- data/lib/rigor/inference/expression_typer.rb +379 -113
- data/lib/rigor/inference/fork_map.rb +87 -0
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +30 -5
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +200 -0
- data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +22 -16
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +33 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +11 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +42 -9
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +5 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +28 -35
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +40 -8
- data/lib/rigor/inference/method_dispatcher.rb +57 -1
- data/lib/rigor/inference/mutation_widening.rb +42 -8
- data/lib/rigor/inference/narrowing.rb +245 -17
- data/lib/rigor/inference/parameter_inference_collector.rb +70 -10
- data/lib/rigor/inference/project_patched_scanner.rb +2 -1
- data/lib/rigor/inference/rbs_type_translator.rb +8 -1
- data/lib/rigor/inference/scope_indexer.rb +460 -49
- data/lib/rigor/inference/statement_evaluator.rb +111 -17
- data/lib/rigor/inference/struct_fold_safety.rb +48 -11
- data/lib/rigor/inference/synthetic_method_scanner.rb +15 -14
- data/lib/rigor/inference/void_origin.rb +25 -0
- data/lib/rigor/inference/void_tail_summary.rb +220 -0
- data/lib/rigor/language_server/completion_provider.rb +2 -1
- data/lib/rigor/language_server/document_symbol_provider.rb +2 -1
- data/lib/rigor/language_server/folding_range_provider.rb +2 -1
- data/lib/rigor/language_server/selection_range_provider.rb +2 -1
- data/lib/rigor/language_server/signature_help_provider.rb +2 -1
- data/lib/rigor/plugin/base.rb +9 -22
- data/lib/rigor/plugin/inflector.rb +12 -3
- data/lib/rigor/plugin/io_boundary.rb +5 -1
- data/lib/rigor/plugin/isolation.rb +81 -11
- data/lib/rigor/plugin/load_error.rb +10 -1
- data/lib/rigor/plugin/loader.rb +96 -14
- data/lib/rigor/plugin/node_rule_walk.rb +5 -3
- data/lib/rigor/plugin/registry.rb +32 -23
- data/lib/rigor/plugin.rb +26 -0
- data/lib/rigor/protection/mutator.rb +3 -2
- data/lib/rigor/reflection.rb +64 -0
- data/lib/rigor/runtime/jit.rb +128 -0
- data/lib/rigor/scope/discovery_index.rb +12 -2
- data/lib/rigor/scope.rb +122 -28
- data/lib/rigor/sig_gen/classification.rb +5 -1
- data/lib/rigor/sig_gen/generator.rb +38 -6
- data/lib/rigor/sig_gen/observation_collector.rb +4 -3
- data/lib/rigor/sig_gen/rbs_validity.rb +44 -0
- data/lib/rigor/sig_gen/renderer.rb +10 -0
- data/lib/rigor/sig_gen/write_result.rb +9 -4
- data/lib/rigor/sig_gen/writer.rb +24 -2
- data/lib/rigor/source/node_children.rb +116 -0
- data/lib/rigor/source/node_locator.rb +3 -1
- data/lib/rigor/source/node_walker.rb +4 -2
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/type/difference.rb +28 -24
- data/lib/rigor/type/hash_shape.rb +34 -10
- data/lib/rigor/version.rb +1 -1
- data/lib/rigortype.rb +24 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +6 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +23 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +5 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +21 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +4 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +13 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +65 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +20 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +4 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +172 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +22 -8
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +3 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +4 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +32 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +38 -6
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +3 -1
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +3 -1
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +10 -0
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +11 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +3 -1
- data/plugins/rigor-rails/lib/rigor-rails.rb +11 -12
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +191 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +3 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +19 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +48 -15
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +42 -12
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +83 -9
- data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +9 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +3 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +3 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +11 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +4 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +4 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +3 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +74 -14
- data/sig/prism_node_children.rbs +9 -0
- data/sig/rigor/environment.rbs +1 -0
- data/sig/rigor/inference.rbs +7 -0
- data/sig/rigor/plugin/base.rbs +2 -3
- data/sig/rigor/reflection.rbs +1 -0
- data/sig/rigor/scope.rbs +12 -1
- data/skills/rigor-ci-setup/SKILL.md +10 -0
- data/skills/rigor-editor-setup/SKILL.md +11 -0
- data/skills/rigor-next-steps/SKILL.md +23 -2
- data/skills/rigor-plugin-author/SKILL.md +3 -3
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +2 -2
- data/skills/rigor-plugin-review/SKILL.md +2 -2
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +5 -4
- data/skills/rigor-project-init/SKILL.md +29 -3
- data/skills/rigor-project-init/references/01-detect.md +1 -1
- data/skills/rigor-project-init/references/02-configure.md +39 -5
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +20 -0
- data/skills/rigor-project-init/references/05-jit-performance.md +125 -0
- metadata +49 -24
- data/lib/rigor/cli/ci_detector.rb +0 -89
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7cea93d0b50519bad210f33907d4135c61043d910618153ad76f1f25b0163b4d
|
|
4
|
+
data.tar.gz: 45cba56af68f2ee34c7e8674310fb35bb74e366a3fc67bc89262ad8cb9556cc7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 626ef3d0d0967cfd0f8e887a7ab2f53ed741a848fd25f90239c69e7a40e84c3b4a658acd945da0fd4d344d9de9cc650c3be8236f107468b70e2d32441ee9d7d3
|
|
7
|
+
data.tar.gz: e23af83633c18c05dce1a01814f235838143a31f3a34cf719ca35239a40bc854fff8f8e4bd55f4d0c4f39547b4b600c7c543019055576e297fed36ef60a70537
|
data/README.md
CHANGED
|
@@ -231,8 +231,8 @@ rigor docs --list # list every bundled page
|
|
|
231
231
|
|
|
232
232
|
## Status
|
|
233
233
|
|
|
234
|
-
Current release: **`v0.
|
|
235
|
-
`0.
|
|
234
|
+
Current release: **`v0.3.0`** (2026-07-19) — the first cut on the
|
|
235
|
+
`0.3.x` line, continuing the evaluation era opened by `v0.2.0`, the first
|
|
236
236
|
publicly-announced (general / evaluation) release. The line publishes
|
|
237
237
|
an enumerated [compatibility surface](docs/compatibility.md) as a
|
|
238
238
|
minor-non-break trial, rehearsing the contract that hard-freezes at
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Rigor core overlay — supplemental core signatures.
|
|
2
|
+
#
|
|
3
|
+
# --- CSV::MalformedCSVError.new(message, line_number) ---
|
|
4
|
+
#
|
|
5
|
+
# The pinned `rbs` gem's `csv.rbs` never declares `MalformedCSVError`'s own
|
|
6
|
+
# constructor, so it falls back to the inherited `StandardError#initialize`
|
|
7
|
+
# (arity 0..1). But the `csv` gem's real, documented constructor takes two
|
|
8
|
+
# arguments — the message and the 1-based line number:
|
|
9
|
+
#
|
|
10
|
+
# raise CSV::MalformedCSVError.new("Unquoted fields...", line)
|
|
11
|
+
#
|
|
12
|
+
# Without the two-arg form a raise like GitLab's
|
|
13
|
+
# `import_csv/base_service.rb` false-fires `call.wrong-arity`
|
|
14
|
+
# ("expected 0..1, got 2"). The line number is optional here so the
|
|
15
|
+
# inherited single-arg form still type-checks.
|
|
16
|
+
|
|
17
|
+
# `CSV` is a CLASS in Ruby / RBS (a `module CSV` wrapper collides with upstream
|
|
18
|
+
# once the `csv` stdlib is in scope). It must be declared EXPLICITLY here — the
|
|
19
|
+
# overlay loads unconditionally, but `csv` is a stdlib library loaded only on
|
|
20
|
+
# demand, so a bare `class CSV::MalformedCSVError` would make RBS *synthesize*
|
|
21
|
+
# the `CSV` parent namespace (ADR-5) in every env that does not load `csv`,
|
|
22
|
+
# polluting `synthesized_namespaces`. Declaring `class CSV` reopens it cleanly
|
|
23
|
+
# when `csv` is loaded and declares it plainly when it is not.
|
|
24
|
+
class CSV
|
|
25
|
+
class MalformedCSVError
|
|
26
|
+
def initialize: (String message, ?Integer line_number) -> void
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Rigor core overlay — supplemental core signatures.
|
|
2
|
+
#
|
|
3
|
+
# --- Psych.parse / Psych.parse_stream ---
|
|
4
|
+
#
|
|
5
|
+
# The pinned `rbs` gem's `psych.rbs` declares `Psych.load` / `load_file` /
|
|
6
|
+
# `safe_load` / `unsafe_load*` but omits `Psych.parse` — a real, documented
|
|
7
|
+
# stdlib method (`YAML.parse` is the same method, `YAML` being an alias of
|
|
8
|
+
# `Psych`). It parses one YAML document into a `Psych::Nodes::Document` AST
|
|
9
|
+
# (used for structure-preserving inspection) and raises on a syntax error.
|
|
10
|
+
#
|
|
11
|
+
# YAML.parse(File.read("config.yml")) # => Psych::Nodes::Document
|
|
12
|
+
#
|
|
13
|
+
# Without the declaration a call like GitLab's
|
|
14
|
+
# `release_highlights/validator.rb` `YAML.parse(...)` false-fires
|
|
15
|
+
# `call.undefined-method`. The return is left `untyped` — the node AST is
|
|
16
|
+
# rarely consumed with per-method precision, and depending on
|
|
17
|
+
# `Psych::Nodes::Document` being present in the pinned RBS would be brittle.
|
|
18
|
+
|
|
19
|
+
module Psych
|
|
20
|
+
def self.parse: (String yaml, ?filename: untyped) -> untyped
|
|
21
|
+
def self.parse_stream: (String yaml, ?filename: untyped) -> untyped
|
|
22
|
+
end
|
|
@@ -87,6 +87,14 @@ silent everywhere it cannot prove a narrower type.
|
|
|
87
87
|
A diagnostic only fires when Rigor has enough static
|
|
88
88
|
information to be confident.
|
|
89
89
|
|
|
90
|
+
Where a *signature* does appear in this book — like
|
|
91
|
+
`def foo: (String) -> Integer` — that is **RBS**, Ruby's standard
|
|
92
|
+
signature language; read it as "takes a `String`, returns an
|
|
93
|
+
`Integer`." You write none of it to get started; Rigor consumes
|
|
94
|
+
whatever RBS your gems and the standard library already ship.
|
|
95
|
+
[Chapter 7](07-rbs-and-extended.md) covers RBS in full — if you
|
|
96
|
+
have never seen it, skim that chapter first.
|
|
97
|
+
|
|
90
98
|
## The smallest working session
|
|
91
99
|
|
|
92
100
|
Drop into your project root and run:
|
|
@@ -191,7 +199,7 @@ assert_type(":int | :str | nil", kind(7)) # union of all case branches
|
|
|
191
199
|
|
|
192
200
|
```ruby
|
|
193
201
|
greeting = "Hello, " # Constant<"Hello, ">
|
|
194
|
-
name = ARGV.first # String? (RBS-declared)
|
|
202
|
+
name = ARGV.first # String? (String or nil — RBS-declared)
|
|
195
203
|
hello = "#{greeting}#{name}!" # literal-string carrier:
|
|
196
204
|
# every interpolated part
|
|
197
205
|
# is itself literal-string-
|
|
@@ -42,7 +42,10 @@ this chapter is the carrier zoo.
|
|
|
42
42
|
One note on notation before the zoo: angle brackets hold a
|
|
43
43
|
concrete value or bound — `Constant<3>`, `int<0, max>` —
|
|
44
44
|
while square brackets hold type parameters, exactly as in RBS
|
|
45
|
-
— `Nominal[String]`, `Hash[K, V]`, `Dynamic[top]`.
|
|
45
|
+
— `Nominal[String]`, `Hash[K, V]`, `Dynamic[top]`. A type
|
|
46
|
+
parameter names the type of a *part*: `Hash[K, V]` is a Hash
|
|
47
|
+
whose keys are type `K` and values type `V`, just as
|
|
48
|
+
`Array[String]` is "an array of strings."
|
|
46
49
|
|
|
47
50
|
## Seeing carriers yourself — `rigor annotate`
|
|
48
51
|
|
|
@@ -275,7 +275,8 @@ end
|
|
|
275
275
|
(Narrowing the truthy edge further to specific refinement
|
|
276
276
|
carriers — so `\d{4}` would produce `decimal-int-string` — is a
|
|
277
277
|
demand-driven follow-up on the regex-pattern → refinement-name
|
|
278
|
-
recogniser track
|
|
278
|
+
recogniser track, tracked as
|
|
279
|
+
[#164](https://github.com/rigortype/rigor/issues/164).)
|
|
279
280
|
|
|
280
281
|
## Negation and `unless`
|
|
281
282
|
|
|
@@ -221,9 +221,13 @@ arr = [1, ARGV.first]
|
|
|
221
221
|
# Array[Constant<1> | String?]
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
The same goes for hashes whose keys are not provably
|
|
225
|
-
|
|
226
|
-
`
|
|
224
|
+
The same goes for hashes whose keys are not provably scalar
|
|
225
|
+
literals (symbols, plain strings, integers, floats, `true` /
|
|
226
|
+
`false` / `nil`) — Rigor produces `Hash[K, V]` rather than
|
|
227
|
+
`HashShape`. Within a shape, key identity follows `Hash#eql?`:
|
|
228
|
+
`1` and `1.0` are distinct keys, and a repeated literal key
|
|
229
|
+
keeps the last entry, exactly like the runtime. Non-symbol,
|
|
230
|
+
non-string keys display in hashrocket form (`{ 1 => 2 }`).
|
|
227
231
|
|
|
228
232
|
## Deriving new shapes — `pick_of` / `omit_of` / `partial_of` / `required_of` / `readonly_of`
|
|
229
233
|
|
data/docs/handbook/06-classes.md
CHANGED
|
@@ -170,8 +170,8 @@ discovery composes uniformly across both shapes.
|
|
|
170
170
|
|
|
171
171
|
`Struct.new(*Symbol)` produces a positional-arg constructor
|
|
172
172
|
plus the same accessors as `Data.define`. Rigor folds struct
|
|
173
|
-
member reads too,
|
|
174
|
-
|
|
173
|
+
member reads too, and — because a `Struct` is mutable — tracks a
|
|
174
|
+
straight-line setter so the read stays precise:
|
|
175
175
|
|
|
176
176
|
```ruby
|
|
177
177
|
Coord = Struct.new(:x, :y)
|
|
@@ -181,18 +181,27 @@ c = Coord.new(10, 20)
|
|
|
181
181
|
assert_type("10", c.x)
|
|
182
182
|
assert_type("20", c.y)
|
|
183
183
|
|
|
184
|
-
# A
|
|
185
|
-
# its reads degrade to Dynamic, never a stale value.
|
|
184
|
+
# A straight-line setter re-types the member; the sibling stays precise.
|
|
186
185
|
m = Coord.new(1, 2)
|
|
187
186
|
m.x = 9
|
|
188
|
-
assert_type("
|
|
187
|
+
assert_type("9", m.x)
|
|
188
|
+
assert_type("2", m.y)
|
|
189
|
+
|
|
190
|
+
# A local that is aliased, escapes, or is mutated inside a loop or
|
|
191
|
+
# block is not fold-safe — its reads degrade to Dynamic, never a
|
|
192
|
+
# stale value, because a write Rigor cannot see could have changed it.
|
|
193
|
+
a = Coord.new(1, 2)
|
|
194
|
+
alias_ref = a
|
|
195
|
+
a.x = 9
|
|
196
|
+
assert_type("Dynamic[top]", a.x)
|
|
189
197
|
```
|
|
190
198
|
|
|
191
199
|
Because `Struct` accessors are also writers, the fold is gated:
|
|
192
|
-
a member read off a freshly constructed instance (`Coord.new(1,
|
|
193
|
-
2).x`)
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
a member read folds off a freshly constructed instance (`Coord.new(1,
|
|
201
|
+
2).x`), off a local the analysis proves is never written, aliased, or
|
|
202
|
+
passed away, or off one whose only writes are straight-line member
|
|
203
|
+
setters (their assigned type flows into the later read); anything else
|
|
204
|
+
widens to `Dynamic[top]`. `Data` is read-only, so its reads always fold.
|
|
196
205
|
|
|
197
206
|
## Inheritance and method resolution
|
|
198
207
|
|
|
@@ -87,6 +87,7 @@ Fire when the control flow itself is unsound.
|
|
|
87
87
|
| `flow.always-truthy-condition` | The predicate of an `if` / `unless` / ternary is provably truthy (or falsey) by inferred type, with surgical skips inside loop bodies and on defensive predicate calls. | warning |
|
|
88
88
|
| `flow.unreachable-clause` | A `case <local>; when <Class>` (or bare-class `case`/`in`) clause whose subject narrowing proves it can never match — disjoint from the subject's type, or already exhausted by an earlier clause. | info under `balanced`, warning under `strict`, info under `lenient` |
|
|
89
89
|
| `flow.dead-assignment` | A plain local-variable write whose target name is never read in the same `def` body. | warning |
|
|
90
|
+
| `flow.duplicate-hash-key` | Two entries of one Hash literal carry the same literal key (`{ a: 1, a: 2 }`, `m("x" => 1, "x" => 2)`) — the last entry wins silently at runtime. Literal keys only; `:a` vs `"a"` and `1` vs `1.0` are distinct keys and never compared. | warning |
|
|
90
91
|
|
|
91
92
|
`flow.unreachable-branch`, `flow.always-truthy-condition`, and
|
|
92
93
|
`flow.unreachable-clause` are the **reachability family** — each
|
|
@@ -142,9 +143,9 @@ shipped severities:
|
|
|
142
143
|
|
|
143
144
|
| Profile | Behaviour |
|
|
144
145
|
| --- | --- |
|
|
145
|
-
| `lenient` |
|
|
146
|
-
| `balanced` (default) | Most rules → `error`; `dump.type` → `info`. The shipped behaviour. |
|
|
147
|
-
| `strict` |
|
|
146
|
+
| `lenient` | Only proven rules stay `error` (`call.undefined-method`, `wrong-arity`, `assert.type-mismatch`); uncertain rules drop to `warning`, and several to `off`. For incremental adoption on legacy code. |
|
|
147
|
+
| `balanced` (default) | Most rules → `error`; uncertain rules → `warning`; `dump.type` → `info`. The shipped behaviour. |
|
|
148
|
+
| `strict` | Nearly every rule → `error`. The exceptions: `call.self-undefined-method` stays `off` (opt-in only), and `flow.unreachable-clause` is `warning` (pending its false-positive gate). Suitable for new projects with no legacy noise. |
|
|
148
149
|
|
|
149
150
|
Set in `.rigor.yml`:
|
|
150
151
|
|
data/docs/handbook/11-sig-gen.md
CHANGED
|
@@ -93,7 +93,7 @@ states:
|
|
|
93
93
|
| `equivalent` | The inferred return is not a strict subtype of the declared one — identical, wider, or unrelated — so there is nothing to tighten. Silently skipped. |
|
|
94
94
|
| `skipped` | Disqualified for one of the reasons below. |
|
|
95
95
|
|
|
96
|
-
The
|
|
96
|
+
The `sig.skipped.*` reasons are:
|
|
97
97
|
|
|
98
98
|
- `sig.skipped.complex-shape` — the method has optional, rest,
|
|
99
99
|
keyword, block, or forwarding parameters. The MVP's
|
|
@@ -105,6 +105,16 @@ The three `sig.skipped.*` reasons are:
|
|
|
105
105
|
- `sig.skipped.user-authored` — `--overwrite` was not set
|
|
106
106
|
and the method's existing RBS declaration would have to
|
|
107
107
|
be replaced.
|
|
108
|
+
- `sig.skipped.unrenderable-rbs` — the signature Rigor
|
|
109
|
+
rendered for this method does not parse as RBS. This one
|
|
110
|
+
is a **bug in Rigor**, not a property of your code: every
|
|
111
|
+
generated line is parsed before it is emitted, and a line
|
|
112
|
+
`rbs` rejects is dropped rather than written, because an
|
|
113
|
+
unparseable `.rbs` is quarantined *whole* by `rigor check`
|
|
114
|
+
— one bad line would take every other type in the file
|
|
115
|
+
down with it. The rest of the signatures are unaffected;
|
|
116
|
+
the skipped method is reported on stderr, and it is worth
|
|
117
|
+
reporting to us.
|
|
108
118
|
|
|
109
119
|
The three `sig.generated.*` identifiers
|
|
110
120
|
(`sig.generated.new-file` / `new-method` / `tighter-return`)
|
|
@@ -345,9 +345,10 @@ def shout: (String s) -> non-empty-string
|
|
|
345
345
|
|
|
346
346
|
You gain: `s.empty?` is a recognised refinement narrower (no
|
|
347
347
|
need for `assert s`). `match?(/\A\d+\z/)` does not yet narrow
|
|
348
|
-
to `numeric-string` (
|
|
349
|
-
[
|
|
350
|
-
behaviour will mirror `s.isdigit()` narrowing in
|
|
348
|
+
to `numeric-string` (queued as
|
|
349
|
+
[#164](https://github.com/rigortype/rigor/issues/164)), but the
|
|
350
|
+
eventual behaviour will mirror `s.isdigit()` narrowing in
|
|
351
|
+
Pyright.
|
|
351
352
|
|
|
352
353
|
## What's next
|
|
353
354
|
|
data/docs/install.md
CHANGED
|
@@ -37,19 +37,27 @@ Then proceed to the **first** matching case below.
|
|
|
37
37
|
and pins language runtimes (Ruby, Node, Python, …) and tool gems
|
|
38
38
|
(like `rigortype`) per project, recording versions in a `mise.toml`
|
|
39
39
|
that can be committed alongside the code. Other contributors — and CI
|
|
40
|
-
— run `mise install` to restore
|
|
41
|
-
|
|
40
|
+
— run `mise install` to restore those versions with no `Gemfile`
|
|
41
|
+
involvement.
|
|
42
42
|
|
|
43
43
|
Run in the project root:
|
|
44
44
|
|
|
45
45
|
```sh
|
|
46
46
|
mise use ruby@4.0
|
|
47
|
-
mise use gem:rigortype
|
|
47
|
+
mise use --pin gem:rigortype
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
`mise use` installs the tools and writes their versions to `mise.toml`
|
|
51
51
|
in one step. Commit `mise.toml` so the version is shared.
|
|
52
52
|
|
|
53
|
+
`--pin` records the exact Rigor version (`"gem:rigortype" = "0.2.9"`).
|
|
54
|
+
Without it mise writes `"gem:rigortype" = "latest"`, which every
|
|
55
|
+
machine re-resolves to whatever is newest when it first installs — so
|
|
56
|
+
a committed `latest` does not give the team one shared version. The
|
|
57
|
+
trade-off: a pin will not move on its own, and `mise outdated` cannot
|
|
58
|
+
report a pinned tool as behind. Upgrade with `mise upgrade --bump
|
|
59
|
+
gem:rigortype`.
|
|
60
|
+
|
|
53
61
|
Then verify:
|
|
54
62
|
|
|
55
63
|
```sh
|
|
@@ -184,6 +192,6 @@ project is set up, re-run `rigor skill describe` for the step after
|
|
|
184
192
|
that.
|
|
185
193
|
|
|
186
194
|
If `rigor skill describe` is not recognised, your Rigor version predates
|
|
187
|
-
it. Run `rigor --version` and upgrade with `mise
|
|
188
|
-
`gem update rigortype` for Case C
|
|
189
|
-
`rigor skill rigor-project-init` directly.
|
|
195
|
+
it. Run `rigor --version` and upgrade with `mise upgrade --bump
|
|
196
|
+
gem:rigortype` (or `gem update rigortype` for Case B/C); on an older
|
|
197
|
+
version, run `rigor skill rigor-project-init` directly.
|
|
@@ -244,9 +244,23 @@ A few things worth knowing if you have not used mise before:
|
|
|
244
244
|
*current directory* recording the chosen versions, and installs
|
|
245
245
|
the tools as part of the same command — there is no separate
|
|
246
246
|
install step. (mise also reads the asdf-style `.tool-versions`.)
|
|
247
|
-
- **Commit the config to share versions
|
|
248
|
-
`mise.toml` into Git so every
|
|
249
|
-
|
|
247
|
+
- **Commit the config to share versions — and pass `--pin` if you
|
|
248
|
+
mean it.** Check the generated `mise.toml` into Git so every
|
|
249
|
+
contributor, and every CI run, resolves the same versions. Note
|
|
250
|
+
what the two commands above actually record, because they differ:
|
|
251
|
+
`mise use ruby@4.0` preserves the precision you asked for and
|
|
252
|
+
writes `ruby = "4.0"` (any 4.0.x), but `mise use gem:rigortype` has
|
|
253
|
+
no requested version to preserve and writes `"gem:rigortype" =
|
|
254
|
+
"latest"` — which resolves to whatever is newest *on each machine,
|
|
255
|
+
whenever that machine first installs it*. A committed `latest` is
|
|
256
|
+
not a shared version. To share one, pin it:
|
|
257
|
+
|
|
258
|
+
```sh
|
|
259
|
+
mise use --pin gem:rigortype # records e.g. "gem:rigortype" = "0.2.9"
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Then read [Keeping Rigor up to date](#keeping-rigor-up-to-date):
|
|
263
|
+
a pin is a version that will not move until you move it.
|
|
250
264
|
- **For a machine-wide install, add `-g`.** `mise use -g
|
|
251
265
|
gem:rigortype` writes mise's global config
|
|
252
266
|
(`~/.config/mise/config.toml`) instead of a project `mise.toml`,
|
|
@@ -284,6 +298,48 @@ explicitly with `mise exec gem:rigortype -- rigor`. See
|
|
|
284
298
|
[Editor integration](09-editor-integration.md) for the editor
|
|
285
299
|
side.
|
|
286
300
|
|
|
301
|
+
### Keeping Rigor up to date
|
|
302
|
+
|
|
303
|
+
Rigor ships often. How you upgrade depends on what your config
|
|
304
|
+
records — and in one case, on knowing that mise will not tell you an
|
|
305
|
+
upgrade exists.
|
|
306
|
+
|
|
307
|
+
- **`"gem:rigortype" = "latest"`** — what a plain `mise use
|
|
308
|
+
gem:rigortype` writes. `mise upgrade gem:rigortype` moves you to the
|
|
309
|
+
newest release.
|
|
310
|
+
- **An exact pin** — `"gem:rigortype" = "0.2.9"`, from `--pin` or from
|
|
311
|
+
`mise use gem:rigortype@0.2.9`. Here `mise upgrade` **will not move
|
|
312
|
+
it, and `mise outdated` will not report it.** Both compare the
|
|
313
|
+
installed version against the range the config asks for, and an
|
|
314
|
+
exact pin is a range containing only itself, so a pinned Rigor
|
|
315
|
+
reports itself up to date forever, however far behind it has fallen.
|
|
316
|
+
Use `--bump`, which installs the newest release *and* rewrites the
|
|
317
|
+
pin:
|
|
318
|
+
|
|
319
|
+
```sh
|
|
320
|
+
mise upgrade --bump gem:rigortype
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
Neither is a bug to work around — a pin doing nothing is a pin working.
|
|
324
|
+
But it does mean a pinned setup has no passive signal that a new Rigor
|
|
325
|
+
exists: `--bump` is both how you look and how you move.
|
|
326
|
+
|
|
327
|
+
**After a Ruby change.** mise's `gem:` backend installs each tool
|
|
328
|
+
against the Ruby that was active at install time, and its
|
|
329
|
+
[gem backend docs](https://mise.jdx.dev/dev-tools/backends/gem.html)
|
|
330
|
+
note that "if the ruby version used by a gem package changes, (by mise
|
|
331
|
+
or system ruby), you may need to reinstall the gem". Rigor's pinned
|
|
332
|
+
`ruby@4.0` makes this rare — patch upgrades within 4.0.x are followed
|
|
333
|
+
automatically — but if you remove or replace that Ruby, reinstall:
|
|
334
|
+
|
|
335
|
+
```sh
|
|
336
|
+
mise install -f gem:rigortype # or, for every gem-backend tool:
|
|
337
|
+
mise install -f "gem:*"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
On a plain `gem install` (below), the equivalent is `gem update
|
|
341
|
+
rigortype`.
|
|
342
|
+
|
|
287
343
|
## asdf
|
|
288
344
|
|
|
289
345
|
`asdf` follows the same model. Install a Ruby 4.0.x with the
|
|
@@ -291,6 +347,7 @@ side.
|
|
|
291
347
|
it for the project, then install the gem into that Ruby:
|
|
292
348
|
|
|
293
349
|
```sh
|
|
350
|
+
asdf plugin add ruby # once, if the ruby plugin isn't added yet
|
|
294
351
|
asdf install ruby latest:4.0
|
|
295
352
|
asdf local ruby latest:4.0
|
|
296
353
|
gem install rigortype
|
|
@@ -299,7 +356,8 @@ gem install rigortype
|
|
|
299
356
|
`asdf` has no general-purpose gem backend, so the gem itself is
|
|
300
357
|
installed with `gem install` rather than an `asdf` command. `mise`
|
|
301
358
|
(above) is the more integrated option because its `gem:` backend
|
|
302
|
-
|
|
359
|
+
records the gem in the same config that records Ruby, and — as the
|
|
360
|
+
next section explains — keeps the two from interfering.
|
|
303
361
|
|
|
304
362
|
## Simple alternative — gem install
|
|
305
363
|
|
|
@@ -315,6 +373,43 @@ quickest path, but it records nothing per project: a version
|
|
|
315
373
|
manager keeps the Rigor version pinned next to the project, so
|
|
316
374
|
local runs and CI cannot drift apart.
|
|
317
375
|
|
|
376
|
+
It also leaves Rigor sharing a Ruby with your work, which is the
|
|
377
|
+
deeper reason the version managers are listed first. The executable
|
|
378
|
+
RubyGems installs begins with `#!/usr/bin/env ruby`, so it runs under
|
|
379
|
+
whatever `ruby` is first on `PATH` *at the moment you invoke it* — and
|
|
380
|
+
because Rigor requires Ruby 4.0 while your projects pin their own,
|
|
381
|
+
running `rigor` inside a project on Ruby 3.x fails before it starts.
|
|
382
|
+
mise's `gem:` backend does not have this failure mode: it installs each
|
|
383
|
+
tool into its own private gem directory and points the executable at
|
|
384
|
+
the Ruby the tool was installed with, so a project's Ruby pin cannot
|
|
385
|
+
reach it. A `gem install` under a Ruby 4.0 you never switch away from
|
|
386
|
+
is perfectly fine; one that shares a Ruby with projects you switch
|
|
387
|
+
between is a footgun.
|
|
388
|
+
|
|
389
|
+
## If you want Bundler to manage the version
|
|
390
|
+
|
|
391
|
+
The rule at the top of this chapter is about your *application's*
|
|
392
|
+
`Gemfile` — the one Bundler resolves against your app's gems, and whose
|
|
393
|
+
entries `Bundler.require` loads at boot. It is not a rule against
|
|
394
|
+
Bundler. A `Gemfile` holding nothing but Rigor, resolved separately and
|
|
395
|
+
selected with `BUNDLE_GEMFILE`, keeps every property the prohibition
|
|
396
|
+
exists to protect: your application's dependency graph never meets
|
|
397
|
+
Rigor's, and your application's Ruby is never constrained by Rigor's.
|
|
398
|
+
|
|
399
|
+
That arrangement — a `Gemfile` under `.github/rigor/`, plus the
|
|
400
|
+
Dependabot config that keeps it current — is written up under
|
|
401
|
+
[Pinning Rigor's version](11-ci.md#pinning-rigors-version) in the CI
|
|
402
|
+
chapter. Nothing about it is CI-specific:
|
|
403
|
+
|
|
404
|
+
```sh
|
|
405
|
+
BUNDLE_GEMFILE=.github/rigor/Gemfile bundle exec rigor check
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
It is more moving parts than `mise use --pin gem:rigortype`, which
|
|
409
|
+
records the same version in one line and needs no `bundle exec`. Reach
|
|
410
|
+
for it when your team already runs everything through Bundler and wants
|
|
411
|
+
Rigor on the same footing — not as a first choice.
|
|
412
|
+
|
|
318
413
|
## Nix
|
|
319
414
|
|
|
320
415
|
If you use Nix, Rigor's flake exposes the executable as a package,
|
|
@@ -41,12 +41,12 @@ the `paths:` list from the configuration file.
|
|
|
41
41
|
| `--cache-stats` | Print the on-disk cache inventory when finished. |
|
|
42
42
|
| `--[no-]stats` | Print a run summary (files, classes, memory, wall time) to stderr. Default on. |
|
|
43
43
|
| `--coverage` | Add a type-precision coverage block to the output (`coverage` object under `--format json`; a one-line summary in text mode). Off by default — it is a second precision pass over the analyzed files, the same scan [`rigor coverage`](#rigor-coverage) runs, so it is opt-in. |
|
|
44
|
-
| `--workers=N` | Dispatch analysis across `N` parallel worker processes (fork-based pool today; ADR-15). Default `0` (sequential). |
|
|
44
|
+
| `--workers=N` | Dispatch analysis across `N` parallel worker processes (fork-based pool today; ADR-15). Default `0` (sequential). Applies to `--incremental` re-checks as well as full runs. |
|
|
45
45
|
| `--baseline=PATH` | Load a baseline file, overriding config. |
|
|
46
46
|
| `--no-baseline` | Ignore any configured baseline. |
|
|
47
47
|
| `--baseline-strict` | Fail the run on any baseline drift — a CI gate. |
|
|
48
48
|
| `--treat-all-as-inline-rbs` | Force-load `rigor-rbs-inline` with `require_magic_comment: false`, so every analysed file is treated as inline-RBS without the `# rbs_inline: enabled` comment (ADR-32). |
|
|
49
|
-
| `--bleeding-edge[=ids]` | Adopt the bleeding-edge overlay for this run, overriding the configured [`bleeding_edge:`](03-configuration.md) selection (ADR-50 § WD2). Bare adopts every queued feature; `--bleeding-edge=a,b` adopts only the named feature ids.
|
|
49
|
+
| `--bleeding-edge[=ids]` | Adopt the bleeding-edge overlay for this run, overriding the configured [`bleeding_edge:`](03-configuration.md) selection (ADR-50 § WD2). Bare adopts every queued feature; `--bleeding-edge=a,b` adopts only the named feature ids. Inspect it with [`rigor show-bleedingedge`](#rigor-show-bleedingedge). |
|
|
50
50
|
| `--no-bleeding-edge` | Ignore any configured `bleeding_edge:` selection for this run (adopt none). |
|
|
51
51
|
| `--tmp-file=PATH --instead-of=PATH` | Editor mode: analyse `PATH` using the buffer in `--tmp-file`. Both required together. |
|
|
52
52
|
|
|
@@ -183,6 +183,17 @@ rigor sig-gen [paths]
|
|
|
183
183
|
| `--new-files` / `--new-methods` / `--tighter-returns` | Emit only that classification. |
|
|
184
184
|
| `--format=text\|json` | Output format. |
|
|
185
185
|
|
|
186
|
+
Every signature is parsed before it is emitted. A method whose
|
|
187
|
+
generated RBS does not parse is **skipped** (`sig.skipped.unrenderable-rbs`)
|
|
188
|
+
and reported on stderr rather than written — an unparseable
|
|
189
|
+
`.rbs` is quarantined *whole* by `rigor check`, so one bad line
|
|
190
|
+
would take every other type in the file down with it. Under
|
|
191
|
+
`--write`, a file whose assembled content does not parse is
|
|
192
|
+
**refused** (the existing file is left untouched) and the command
|
|
193
|
+
exits `1`: you asked for a write and did not get one. Such a skip
|
|
194
|
+
is a bug in Rigor's RBS rendering, not in your code — please
|
|
195
|
+
report it.
|
|
196
|
+
|
|
186
197
|
## `rigor lsp`
|
|
187
198
|
|
|
188
199
|
Run the Language Server over stdio. See
|
|
@@ -279,6 +290,10 @@ inferring".
|
|
|
279
290
|
rigor coverage [paths]
|
|
280
291
|
```
|
|
281
292
|
|
|
293
|
+
`paths` are files or directories; when omitted, Rigor uses the
|
|
294
|
+
`paths:` list from the configuration file (default `lib`), the same
|
|
295
|
+
as [`rigor check`](#rigor-check).
|
|
296
|
+
|
|
282
297
|
`--format=text|json` selects the output format and
|
|
283
298
|
`--config=PATH` overrides config discovery. `--threshold=RATIO`
|
|
284
299
|
exits `1` when the precision ratio falls below `RATIO`
|
|
@@ -295,7 +310,13 @@ ranked "add a type here" list (the methods most often called on
|
|
|
295
310
|
an untyped receiver), then the least-protected files;
|
|
296
311
|
`--threshold` and `--format=json` work the same. It is a sound
|
|
297
312
|
upper bound on real protection — a concrete receiver is necessary
|
|
298
|
-
but not sufficient for a diagnostic to fire.
|
|
313
|
+
but not sufficient for a diagnostic to fire. `--workers=N`
|
|
314
|
+
fork-parallelizes the protection scan (both the parameter-inference
|
|
315
|
+
pre-pass and the per-file scan) with output byte-identical to a
|
|
316
|
+
sequential run; it resolves the worker count the same way `check`
|
|
317
|
+
does — `--workers` › `RIGOR_RACTOR_WORKERS` ›
|
|
318
|
+
[`parallel.workers:`](03-configuration.md) › `0` (sequential
|
|
319
|
+
default).
|
|
299
320
|
|
|
300
321
|
Adding `--mutation` (with `--protection`) switches to the
|
|
301
322
|
**effectiveness** tier: it measures whether Rigor *does* catch a
|
|
@@ -390,6 +411,11 @@ Report the activation status of every plugin configured in
|
|
|
390
411
|
`.rigor.yml` — loaded, load-error (with reason), and each
|
|
391
412
|
plugin's declared extension surfaces. See [Plugins](07-plugins.md).
|
|
392
413
|
|
|
414
|
+
Each loaded plugin's row also reports the resolved file it
|
|
415
|
+
loaded from (a `path:` line in text, a `"path"` key in JSON), so
|
|
416
|
+
if a stale installed gem is shadowing a newer checkout's bundled
|
|
417
|
+
plugin copy the mismatch is visible at a glance.
|
|
418
|
+
|
|
393
419
|
```sh
|
|
394
420
|
rigor plugins [--format=text|json] [--strict] [--capabilities] [--config=PATH]
|
|
395
421
|
```
|
|
@@ -403,7 +429,7 @@ catalogue** ([ADR-37](../adr/37-plugin-interface-segregation.md)):
|
|
|
403
429
|
a focused, machine-readable map of what each loaded plugin
|
|
404
430
|
contributes — the AST node types its `node_rule`s match, the
|
|
405
431
|
receiver classes its `dynamic_return`s gate on, the methods its
|
|
406
|
-
`narrowing_facts`
|
|
432
|
+
`narrowing_facts` hooks narrow, and the facts it `produces` /
|
|
407
433
|
`consumes`. Combine with `--format=json` for tooling (an AI
|
|
408
434
|
agent can enumerate every plugin's behaviour without reading a
|
|
409
435
|
line of plugin source). The same narrow surfaces also appear in
|
|
@@ -464,8 +490,9 @@ rigor skill [<name>] [--full <name>] [--path <name>] [--list] [--describe]
|
|
|
464
490
|
| `--describe` | Probe the project's state (config / baseline / `sig/` / CI — presence only, never runs `rigor check`) and recommend the next skill to run. Also spelled `describe`; surfaced top-level as [`rigor describe`](#rigor-describe) below. |
|
|
465
491
|
|
|
466
492
|
The verb spellings `rigor skill list` / `print <name>` / `path <name>`
|
|
467
|
-
|
|
468
|
-
|
|
493
|
+
were **removed in v0.3.0** — the positional slot is a skill name, so
|
|
494
|
+
they now read as an unknown skill. Use the forms above.
|
|
495
|
+
`describe` / `--describe` stay first-class.
|
|
469
496
|
|
|
470
497
|
## `rigor describe`
|
|
471
498
|
|
|
@@ -508,8 +535,9 @@ rigor docs [<name>] [--path <name>] [--list [<category>]]
|
|
|
508
535
|
| `--path <name>` | Print the single-line absolute path of a doc, suitable as input to a file-reading tool. |
|
|
509
536
|
| `--list [<category>]` | Table of every bundled doc (name + absolute path); pass `manual` or `handbook` to filter. |
|
|
510
537
|
|
|
511
|
-
The verb spellings `rigor docs list` / `path <name>`
|
|
512
|
-
|
|
538
|
+
The verb spellings `rigor docs list` / `path <name>` were **removed in
|
|
539
|
+
v0.3.0** — the positional slot is a doc name, so they now read as an
|
|
540
|
+
unknown doc. Use `--list` / `--path`.
|
|
513
541
|
|
|
514
542
|
The canonical web copy of the index is
|
|
515
543
|
<https://rigor.typedduck.fail/llms.txt>; `rigor docs` serves the same
|
|
@@ -533,12 +561,16 @@ rigor show-bleedingedge [--config PATH] [--format text|json]
|
|
|
533
561
|
| `--config PATH` | Use this `.rigor.yml` instead of auto-discovery. |
|
|
534
562
|
| `--format text\|json` | Output format. Default `text`. |
|
|
535
563
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
564
|
+
Each queued feature appears with its stable id, the severity it imposes,
|
|
565
|
+
and whether your configuration adopts it. See
|
|
566
|
+
[`docs/compatibility.md`](../compatibility.md) for how bleeding-edge fits
|
|
567
|
+
the stability model.
|
|
568
|
+
|
|
569
|
+
Queued today:
|
|
570
|
+
|
|
571
|
+
| Feature id | What it changes |
|
|
572
|
+
| --- | --- |
|
|
573
|
+
| `reject-unparseable-signatures` | An unparseable `.rbs` under `signature_paths:` **fails the run** (`rbs.coverage.quarantined-signature` → `error`) instead of being skipped with a warning. |
|
|
542
574
|
|
|
543
575
|
## `rigor doctor`
|
|
544
576
|
|
|
@@ -557,8 +589,7 @@ rigor doctor [--config PATH] [--format text|json]
|
|
|
557
589
|
Runs a scoped analysis and audits:
|
|
558
590
|
|
|
559
591
|
- **Configuration audit** — unresolved `signature_paths:`, unknown
|
|
560
|
-
`libraries:`, inert `disable:` / `severity_overrides:` tokens
|
|
561
|
-
({ConfigAudit}).
|
|
592
|
+
`libraries:`, inert `disable:` / `severity_overrides:` tokens.
|
|
562
593
|
- **RBS environment health** — whether the RBS class universe built
|
|
563
594
|
successfully (`0` classes means a broken setup).
|
|
564
595
|
- **Plugin load errors** — whether every configured plugin loaded.
|
|
@@ -566,9 +597,21 @@ Runs a scoped analysis and audits:
|
|
|
566
597
|
from the saved baseline.
|
|
567
598
|
- **Rails plugin gap** — whether `Gemfile.lock` contains Rails gems
|
|
568
599
|
but no Rails plugin is enabled.
|
|
600
|
+
- **Gemfile install** — whether Rigor itself resolves as one of the
|
|
601
|
+
project's dependencies, which
|
|
602
|
+
[Installing Rigor](01-installation.md) tells you not to do. Only a
|
|
603
|
+
`rigortype` resolved from a **GEM** remote counts: a `PATH` or `GIT`
|
|
604
|
+
source means you are developing or vendoring Rigor deliberately,
|
|
605
|
+
and Rigor's own repository looks exactly like that.
|
|
606
|
+
- **Plugin installation skew** — whether a plugin Rigor bundles
|
|
607
|
+
loaded from a different `rigortype` installation than the engine,
|
|
608
|
+
naming both paths. The engine and its bundled plugins are
|
|
609
|
+
versioned together, so a copy from another installation can run
|
|
610
|
+
the engine with a mismatched plugin. A warning, not a failure;
|
|
611
|
+
third-party plugins from your own bundle are never flagged.
|
|
569
612
|
|
|
570
613
|
Text output prints `[PASS]`, `[FAIL]`, or `[WARN]` per check plus a
|
|
571
|
-
routed hint (e.g. "Run `rigor baseline regenerate`").
|
|
614
|
+
routed hint (e.g. "Run `rigor baseline regenerate`"). JSON output
|
|
572
615
|
is a stable contract:
|
|
573
616
|
|
|
574
617
|
```json
|
|
@@ -585,7 +628,7 @@ Exits `1` when any check fails, `0` when all pass.
|
|
|
585
628
|
## `rigor upgrade`
|
|
586
629
|
|
|
587
630
|
Migration command skeleton ([ADR-50](../adr/50-release-engineering-and-stability-strategy.md)
|
|
588
|
-
WD7).
|
|
631
|
+
WD7). The real body lands when a concrete backwards-compatibility
|
|
589
632
|
break gives it a target (e.g. re-running `baseline regenerate`
|
|
590
633
|
against a strengthened default profile, surfacing renamed
|
|
591
634
|
suppression ids, reporting `bleeding_edge:` graduations).
|
|
@@ -595,7 +638,7 @@ rigor upgrade
|
|
|
595
638
|
```
|
|
596
639
|
|
|
597
640
|
Until then it prints the current version and notes that upgrade is
|
|
598
|
-
queued.
|
|
641
|
+
queued. Exits `0`.
|
|
599
642
|
|
|
600
643
|
## Environment variables
|
|
601
644
|
|
|
@@ -609,6 +652,9 @@ operational knobs read the environment instead.
|
|
|
609
652
|
| `RIGOR_RACTOR_WORKERS=N` | Worker count for parallel analysis. Sits between the CLI flag and the config key in precedence: `--workers=N` > `RIGOR_RACTOR_WORKERS` > `parallel.workers:` > `0` (sequential). |
|
|
610
653
|
| `RIGOR_POOL_BACKEND=ractor` | Opt back into the (off-by-default) Ractor worker pool instead of the active fork-based pool ([ADR-15](../adr/15-ractor-concurrency.md)). Only relevant with a non-zero worker count; the fork pool is the supported backend. |
|
|
611
654
|
| `RIGOR_PLUGIN_ISOLATION=none\|process\|ruby_box` | How a plugin's direct calls into its target library are isolated. Default `process`. See [Using plugins § Isolation strategy](07-plugins.md). `RIGOR_BOX` is a legacy alias for `ruby_box`. |
|
|
655
|
+
| `RIGOR_STRICT_VALIDATION=1` | Force full-content cache validation for one run (the same as `cache.validation: digest`, and winning over it) — re-hash every file's content instead of trusting its stat metadata. Use it if a filesystem's timestamps or inode numbers cannot be trusted. See [Caching § How a file is checked for changes](12-caching.md#how-a-file-is-checked-for-changes). |
|
|
656
|
+
| `RIGOR_DISABLE_YJIT=1` | Opt out of Rigor's deferred YJIT enablement. Rigor turns YJIT on partway through a long `check` / `coverage` run so short runs never pay the JIT warm-up; this variable leaves it off entirely. Diagnostics and allocations are identical either way — the effect is wall-time only. |
|
|
657
|
+
| `RIGOR_YJIT_DEADLINE=<seconds>` | Advanced: tune how long a run must last before deferred YJIT enables (default `5.0`). Lower it if your runs are long and you want the JIT sooner; raise it to protect short runs. Ignored when `RIGOR_DISABLE_YJIT=1` is set or YJIT is unavailable. |
|
|
612
658
|
|
|
613
659
|
Three further variables (`RIGOR_BUDGET_TRACE`,
|
|
614
660
|
`RIGOR_HEAP_PROFILE`, `RIGOR_HEAP_TRACE`) enable developer-facing
|