rigortype 0.3.8 → 0.3.9
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 +1 -1
- data/data/capability_roles/capability_roles.rbs +36 -0
- data/data/gem_overlay/activesupport/core_ext.rbs +109 -13
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +37 -4
- data/docs/handbook/05-methods-and-blocks.md +1 -1
- data/docs/handbook/06-classes.md +2 -2
- data/docs/handbook/07-rbs-and-extended.md +14 -1
- data/docs/handbook/11-sig-gen.md +72 -1
- data/docs/handbook/README.md +1 -1
- data/docs/handbook/appendix-elixir.md +2 -2
- data/docs/handbook/appendix-go.md +2 -2
- data/docs/handbook/appendix-java-csharp.md +2 -2
- data/docs/handbook/appendix-mypy.md +2 -2
- data/docs/handbook/appendix-phpstan.md +1 -1
- data/docs/handbook/appendix-rust.md +1 -1
- data/docs/handbook/appendix-type-theory.md +4 -4
- data/docs/handbook/appendix-typescript.md +1 -1
- data/docs/llms.txt +2 -0
- data/docs/manual/02-cli-reference.md +32 -4
- data/docs/manual/03-configuration.md +11 -0
- data/docs/manual/04-diagnostics.md +11 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/11-ci.md +9 -0
- data/docs/manual/15-type-protection-coverage.md +8 -0
- data/docs/manual/16-rbs-extended-annotations.md +6 -1
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/plugins/rigor-actionpack.md +18 -0
- data/docs/manual/plugins/rigor-activerecord.md +28 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +32 -2
- data/docs/manual/plugins/rigor-rbs-inline.md +42 -1
- data/lib/rigor/analysis/baseline.rb +2 -2
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/dead_version_guard_arms.rb +1 -5
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +39 -4
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +1 -1
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +200 -32
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/builder.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +0 -2
- data/lib/rigor/analysis/dependency_source_inference/index.rb +5 -5
- data/lib/rigor/analysis/dependency_source_inference/return_type_heuristic.rb +1 -2
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/effects_cache_probe.rb +3 -4
- data/lib/rigor/analysis/erb_template_detector.rb +1 -2
- data/lib/rigor/analysis/fact_store.rb +6 -1
- data/lib/rigor/analysis/incremental.rb +16 -0
- data/lib/rigor/analysis/incremental_session.rb +81 -22
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +6 -5
- data/lib/rigor/analysis/reachability/project_files.rb +2 -2
- data/lib/rigor/analysis/reachability/scan.rb +5 -5
- data/lib/rigor/analysis/reachability/scan_cache.rb +2 -2
- data/lib/rigor/analysis/reachability/signature_scan.rb +2 -2
- data/lib/rigor/analysis/result.rb +1 -3
- data/lib/rigor/analysis/rule_catalog.rb +4 -1
- data/lib/rigor/analysis/run_cache_key.rb +1 -1
- data/lib/rigor/analysis/run_cache_probe.rb +3 -5
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +6 -8
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -2
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +177 -26
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +9 -10
- data/lib/rigor/analysis/runner/pool_coordinator.rb +256 -88
- data/lib/rigor/analysis/runner/project_pre_passes.rb +12 -6
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +133 -35
- data/lib/rigor/analysis/worker_session.rb +9 -10
- data/lib/rigor/bleeding_edge.rb +12 -24
- data/lib/rigor/builtins/hkt_builtins.rb +2 -2
- data/lib/rigor/builtins/imported_refinements.rb +155 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +4 -4
- data/lib/rigor/builtins/static_return_refinements.rb +5 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +58 -20
- data/lib/rigor/cache/engine_source.rb +4 -4
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +40 -6
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +0 -3
- data/lib/rigor/cache/rbs_class_type_param_names.rb +0 -3
- data/lib/rigor/cache/rbs_constant_table.rb +0 -3
- data/lib/rigor/cache/rbs_descriptor.rb +79 -11
- data/lib/rigor/cache/rbs_environment.rb +8 -4
- data/lib/rigor/cache/rbs_known_class_names.rb +0 -3
- data/lib/rigor/cache/store.rb +77 -32
- data/lib/rigor/cli/annotate_command.rb +5 -6
- data/lib/rigor/cli/check_command.rb +57 -10
- data/lib/rigor/cli/check_invocation.rb +6 -11
- data/lib/rigor/cli/check_runner_factory.rb +1 -5
- data/lib/rigor/cli/coverage_command.rb +1 -1
- data/lib/rigor/cli/coverage_mutation.rb +12 -3
- data/lib/rigor/cli/coverage_scan.rb +1 -5
- data/lib/rigor/cli/diff_command.rb +1 -1
- data/lib/rigor/cli/doc_links.rb +3 -3
- data/lib/rigor/cli/docs_command.rb +2 -2
- data/lib/rigor/cli/doctor_command.rb +85 -38
- data/lib/rigor/cli/effects_command.rb +2 -2
- data/lib/rigor/cli/effects_diff_renderer.rb +3 -3
- data/lib/rigor/cli/effects_snapshot_command.rb +1 -1
- data/lib/rigor/cli/explain_command.rb +34 -1
- data/lib/rigor/cli/fused_protection_report.rb +7 -1
- data/lib/rigor/cli/lsp_command.rb +1 -1
- data/lib/rigor/cli/mcp_command.rb +1 -1
- data/lib/rigor/cli/measurement_integrity_warning.rb +4 -5
- data/lib/rigor/cli/mutation_fork_scan.rb +6 -6
- data/lib/rigor/cli/mutation_protection_report.rb +7 -1
- data/lib/rigor/cli/plugin_command.rb +1 -1
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +2 -2
- data/lib/rigor/cli/protection_fork_scan.rb +6 -6
- data/lib/rigor/cli/show_bleedingedge_command.rb +1 -1
- data/lib/rigor/cli/sig_gen_command.rb +2 -2
- data/lib/rigor/cli/skill_command.rb +1 -1
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +36 -35
- data/lib/rigor/cli/trace_command.rb +4 -4
- data/lib/rigor/cli/trace_renderer.rb +5 -6
- data/lib/rigor/cli/triage_command.rb +1 -1
- data/lib/rigor/cli/type_of_command.rb +10 -6
- data/lib/rigor/cli/type_scan_command.rb +4 -4
- data/lib/rigor/cli/unused_command.rb +11 -3
- data/lib/rigor/cli/upgrade_command.rb +1 -1
- data/lib/rigor/cli.rb +68 -6
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +6 -6
- data/lib/rigor/configuration.rb +43 -4
- data/lib/rigor/effects/attribution.rb +1 -3
- data/lib/rigor/effects/config_envelopes.rb +6 -8
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +0 -1
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +8 -8
- data/lib/rigor/effects/envelope_index.rb +5 -8
- data/lib/rigor/effects/framework_units.rb +4 -6
- data/lib/rigor/effects/identity.rb +4 -6
- data/lib/rigor/effects/inline_anchor.rb +7 -7
- data/lib/rigor/effects/label_intent.rb +3 -4
- data/lib/rigor/effects/liskov_check.rb +8 -8
- data/lib/rigor/effects/method_key.rb +1 -1
- data/lib/rigor/effects/plugin_facts.rb +4 -6
- data/lib/rigor/effects/propagator.rb +2 -3
- data/lib/rigor/effects/scanner.rb +1 -1
- data/lib/rigor/effects/signature_sources.rb +3 -5
- data/lib/rigor/effects/snapshot.rb +10 -10
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/unit_scan.rb +10 -10
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/environment/bundle_sig_discovery.rb +6 -6
- data/lib/rigor/environment/class_registry.rb +3 -1
- data/lib/rigor/environment/failure_slot.rb +2 -2
- data/lib/rigor/environment/installed_gem_set.rb +85 -0
- data/lib/rigor/environment/lockfile_resolver.rb +51 -4
- data/lib/rigor/environment/missing_gem_constant_index.rb +4 -4
- data/lib/rigor/environment/rbs_collection_discovery.rb +5 -5
- data/lib/rigor/environment/rbs_coverage_report.rb +5 -5
- data/lib/rigor/environment/rbs_hierarchy.rb +3 -1
- data/lib/rigor/environment/rbs_loader.rb +388 -59
- data/lib/rigor/environment.rb +105 -20
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/inference/acceptance.rb +153 -8
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +5 -5
- data/lib/rigor/inference/budget_trace.rb +1 -5
- data/lib/rigor/inference/captured_locals.rb +2 -3
- data/lib/rigor/inference/closure_escape_analyzer.rb +2 -4
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +168 -0
- data/lib/rigor/inference/expression_typer.rb +763 -143
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/hkt_reducer.rb +2 -3
- data/lib/rigor/inference/hkt_registry.rb +4 -7
- data/lib/rigor/inference/index_write_widening.rb +1 -5
- data/lib/rigor/inference/macro_block_self_type.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +2 -9
- data/lib/rigor/inference/method_dispatcher/cgi_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +305 -19
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/json_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -1
- data/lib/rigor/inference/method_dispatcher/math_folding.rb +41 -3
- data/lib/rigor/inference/method_dispatcher/method_folding.rb +2 -5
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +61 -47
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +165 -31
- data/lib/rigor/inference/method_dispatcher/receiver_affinity.rb +25 -4
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +2 -8
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shellwords_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +4 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +85 -4
- data/lib/rigor/inference/method_dispatcher/time_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/universal_object_dispatch.rb +1 -2
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +12 -13
- data/lib/rigor/inference/method_parameter_binder.rb +4 -6
- data/lib/rigor/inference/multi_target_binder.rb +1 -3
- data/lib/rigor/inference/mutation_rejoin.rb +168 -0
- data/lib/rigor/inference/mutation_widening.rb +100 -51
- data/lib/rigor/inference/narrowing.rb +299 -46
- data/lib/rigor/inference/optimistic_origin.rb +1 -7
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_patched_methods.rb +2 -2
- data/lib/rigor/inference/project_patched_scanner.rb +3 -3
- data/lib/rigor/inference/protection_scanner.rb +1 -2
- data/lib/rigor/inference/range_constant.rb +57 -0
- data/lib/rigor/inference/rbs_type_translator.rb +49 -11
- data/lib/rigor/inference/receiver_alias.rb +3 -3
- data/lib/rigor/inference/refinement_mutation.rb +72 -0
- data/lib/rigor/inference/scope_indexer.rb +688 -151
- data/lib/rigor/inference/statement_evaluator.rb +221 -50
- data/lib/rigor/inference/string_mutation.rb +60 -0
- data/lib/rigor/inference/struct_fold_safety.rb +6 -7
- data/lib/rigor/inference/synthetic_method_index.rb +1 -2
- data/lib/rigor/inference/synthetic_method_scanner.rb +4 -6
- data/lib/rigor/inference/version_guard.rb +28 -21
- data/lib/rigor/inference/void_origin.rb +3 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -4
- data/lib/rigor/language_server/buffer_table.rb +4 -4
- data/lib/rigor/language_server/completion_provider.rb +1 -1
- data/lib/rigor/language_server/debouncer.rb +1 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -1
- data/lib/rigor/language_server/hover_provider.rb +1 -1
- data/lib/rigor/language_server/hover_renderer.rb +2 -2
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +2 -5
- data/lib/rigor/language_server/publish_batcher.rb +2 -2
- data/lib/rigor/language_server/selection_range_provider.rb +2 -2
- data/lib/rigor/language_server/server.rb +9 -9
- data/lib/rigor/language_server/signature_help_provider.rb +1 -1
- data/lib/rigor/language_server/uri.rb +1 -1
- data/lib/rigor/plugin/base.rb +90 -21
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +7 -7
- data/lib/rigor/plugin/effect_entry_points.rb +3 -3
- data/lib/rigor/plugin/fact_store.rb +6 -6
- data/lib/rigor/plugin/io_boundary.rb +93 -8
- data/lib/rigor/plugin/isolation.rb +29 -6
- data/lib/rigor/plugin/loader.rb +31 -12
- data/lib/rigor/plugin/macro/heredoc_template.rb +1 -1
- data/lib/rigor/plugin/macro/trait_registry.rb +1 -1
- data/lib/rigor/plugin/manifest.rb +23 -1
- data/lib/rigor/plugin/registry.rb +42 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +12 -1
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin_gap_advisory.rb +96 -0
- data/lib/rigor/project_environment.rb +138 -0
- data/lib/rigor/protection/analysis_guard.rb +93 -14
- data/lib/rigor/protection/closure_kill_oracle.rb +67 -23
- data/lib/rigor/protection/dependency_closure.rb +5 -8
- data/lib/rigor/protection/diagnostic_oracle.rb +2 -2
- data/lib/rigor/protection/discovery_seed.rb +11 -11
- data/lib/rigor/protection/kill_signature.rb +2 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +6 -9
- data/lib/rigor/protection/mutation_scanner.rb +12 -15
- data/lib/rigor/protection/mutator.rb +2 -1
- data/lib/rigor/protection/test_suite_oracle.rb +5 -5
- data/lib/rigor/rbs_extended/conformance_checker.rb +4 -3
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/reporter.rb +36 -10
- data/lib/rigor/rbs_extended.rb +61 -21
- data/lib/rigor/reflection/constant_path.rb +126 -0
- data/lib/rigor/reflection.rb +26 -10
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +30 -0
- data/lib/rigor/scope.rb +203 -4
- data/lib/rigor/sig_gen/generator.rb +115 -22
- data/lib/rigor/sig_gen/layout_index.rb +3 -4
- data/lib/rigor/sig_gen/meta_class_shape.rb +3 -5
- data/lib/rigor/sig_gen/observation_collector.rb +17 -15
- data/lib/rigor/sig_gen/path_mapper.rb +4 -6
- data/lib/rigor/sig_gen/rbs_validity.rb +4 -4
- data/lib/rigor/sig_gen/renderer.rb +4 -5
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +111 -0
- data/lib/rigor/sig_gen/superclass_spelling.rb +27 -0
- data/lib/rigor/sig_gen/type_elaborator.rb +1 -3
- data/lib/rigor/sig_gen/writer.rb +23 -6
- data/lib/rigor/signature_path_audit.rb +153 -6
- data/lib/rigor/source/literals.rb +0 -23
- data/lib/rigor/source/node_children.rb +0 -2
- data/lib/rigor/source/node_locator.rb +5 -11
- data/lib/rigor/source/node_walker.rb +2 -5
- data/lib/rigor/triage/catalogue.rb +1 -3
- data/lib/rigor/triage.rb +3 -5
- data/lib/rigor/type/accepts_result.rb +24 -6
- data/lib/rigor/type/combinator.rb +68 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +5 -5
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +3 -3
- data/lib/rigor/type/struct_class.rb +3 -3
- data/lib/rigor/type/struct_instance.rb +4 -4
- data/lib/rigor/type.rb +1 -0
- data/lib/rigor/type_node/generic.rb +1 -1
- data/lib/rigor/type_node/range_literal.rb +25 -0
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node.rb +1 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +0 -4
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +0 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +0 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -0
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +0 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +3 -5
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +1 -4
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +33 -30
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -3
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +28 -2
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +0 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +0 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/recurring_scan.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +1 -0
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +3 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/structure_sql_parser.rb +1 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +42 -7
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +1 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +9 -2
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +119 -14
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +1 -0
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +9 -7
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +7 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +1 -0
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +8 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +11 -7
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +13 -11
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +9 -4
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +1 -0
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +0 -5
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +8 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +36 -4
- data/plugins/rigor-ffi-rzmq/lib/rigor/plugin/ffi_rzmq.rb +1 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +6 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +1 -0
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +0 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -0
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +0 -5
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/authorization_scan.rb +2 -2
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +0 -1
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +1 -0
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +0 -12
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +1 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +0 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +1 -0
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/acronyms.rb +5 -5
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +3 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/grape_api_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +2 -2
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +9 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -0
- data/plugins/rigor-railties/lib/rigor/plugin/railties.rb +1 -0
- data/plugins/rigor-rbnacl/lib/rigor/plugin/rbnacl.rb +13 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +128 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +0 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -5
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +2 -3
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -0
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +0 -3
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +1 -0
- data/plugins/rigor-sassc/lib/rigor/plugin/sassc.rb +1 -0
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +1 -9
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +1 -0
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +0 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/effects.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/schedule_scan.rb +1 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +0 -1
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +0 -2
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +1 -0
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -0
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +2 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +1 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +3 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +4 -4
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +1 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +5 -5
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/environment.rbs +5 -0
- data/sig/rigor/inference.rbs +3 -10
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/manifest.rbs +2 -1
- data/sig/rigor/reflection.rbs +3 -1
- data/sig/rigor/scope.rbs +24 -3
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +14 -0
- data/sig/rigor/type.rbs +27 -1
- data/sig/rigor.rbs +1 -0
- data/skills/rigor-ask/SKILL.md +8 -6
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +13 -2
- data/skills/rigor-project-init/SKILL.md +6 -3
- data/skills/rigor-project-init/references/02-configure.md +10 -6
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-type-oracle/SKILL.md +228 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +261 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +128 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +8 -5
- metadata +26 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rigor-type-oracle
|
|
3
|
+
description: |
|
|
4
|
+
Before writing or asserting ANY Ruby type, get it from Rigor rather than from reading the code: `rigor type-of FILE:LINE:COL` / `rigor annotate FILE` for an expression, `rigor sig-gen --print FILE` for a method signature, call-site observation for a parameter. A type you did not obtain from Rigor is a guess, and a guessed type is never written anywhere. Triggers: writing RBS under `sig/`, an inline `#:` / `# @rbs` annotation, a Sorbet `sig do … end`, a YARD `@param` / `@return`, a type stated in a doc sentence or a review comment, a nil check / `is_a?` / `respond_to?` guard justified by "this should be an X", "add types to this class / file", "document this method", "what type is this / what does this return?". Applies to Rigor's own tree too. When Rigor answers `Dynamic[top]` or `untyped`, or `sig-gen` skips the method, report the gap — never fill it in from inference of your own. NOT for setting Rigor up (use rigor-next-steps) or working a baseline down (use rigor-baseline-reduce).
|
|
5
|
+
license: MPL-2.0
|
|
6
|
+
metadata:
|
|
7
|
+
version: 0.1.0
|
|
8
|
+
homepage: https://github.com/rigortype/rigor
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Rigor Type Oracle
|
|
12
|
+
|
|
13
|
+
You are about to write a type. Stop and ask Rigor first.
|
|
14
|
+
|
|
15
|
+
This project has a deterministic type oracle installed. Reading the
|
|
16
|
+
source and concluding "`currency` is a String, `pattern` is a Regexp" is
|
|
17
|
+
*plausible-guess* behaviour: it is right often enough to feel safe and
|
|
18
|
+
wrong often enough to poison a signature file, a doc comment, or a
|
|
19
|
+
reviewer's mental model. Rigor already knows the answer — for every
|
|
20
|
+
expression, at every line, from the same engine that will check the code
|
|
21
|
+
tomorrow. Asking it costs one command.
|
|
22
|
+
|
|
23
|
+
**The rule: a type you did not obtain from Rigor is a guess, and a
|
|
24
|
+
guessed type is never written anywhere.** Not in `sig/`, not in an inline
|
|
25
|
+
annotation (`#:`, `# @rbs`), not in a Sorbet `sig`, not in a YARD tag,
|
|
26
|
+
not in a doc sentence, not in a review comment, and not as the stated
|
|
27
|
+
reason for a nil check or an `is_a?` guard.
|
|
28
|
+
|
|
29
|
+
## First: load the version-current copy
|
|
30
|
+
|
|
31
|
+
This skill's exact commands, flags, output spellings, and skip-reason ids
|
|
32
|
+
drift between Rigor releases, so follow the copy that ships with the
|
|
33
|
+
**installed** Rigor rather than any vendored or frozen copy of this file.
|
|
34
|
+
Get the complete current procedure in one call:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
rigor skill --full rigor-type-oracle # this body + all its references/, inline
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
41
|
+
copy — just proceed (read any `references/NN-*.md` from the directory the
|
|
42
|
+
header names). If `rigor` is not on `PATH`, this task needs it: run
|
|
43
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
44
|
+
|
|
45
|
+
## When to use
|
|
46
|
+
|
|
47
|
+
Trigger on the *act*, not on the user saying the word "type". You are in
|
|
48
|
+
scope the moment you are about to write or assert one:
|
|
49
|
+
|
|
50
|
+
- Writing or editing RBS under `sig/`.
|
|
51
|
+
- Writing an inline annotation — rbs-inline `#:`, `# @rbs`, a
|
|
52
|
+
`%a{rigor:v1:…}` override.
|
|
53
|
+
- Writing a Sorbet `sig do … end`, or any other type DSL.
|
|
54
|
+
- Writing a YARD / RDoc `@param` / `@return` / `@raise`, or any doc
|
|
55
|
+
sentence that names a type ("returns an Array of entries").
|
|
56
|
+
- Stating a type in a review comment, a PR description, or a chat answer.
|
|
57
|
+
- Justifying a nil check, an `is_a?` / `respond_to?` guard, or a
|
|
58
|
+
defensive `to_s` with "this should be an X".
|
|
59
|
+
- Being asked "what type is this?", "what does this method return?",
|
|
60
|
+
"add types to this class", "document this file".
|
|
61
|
+
|
|
62
|
+
It applies to **Rigor's own tree** as well: `lib/`, the bundled plugins,
|
|
63
|
+
and the examples are held to the same rule, and a gap found there is
|
|
64
|
+
engine signal worth more than the annotation you would have written.
|
|
65
|
+
|
|
66
|
+
## When NOT to use
|
|
67
|
+
|
|
68
|
+
- **Setting Rigor up on a project that has none** → `rigor-next-steps`
|
|
69
|
+
(which routes to `rigor-project-init`). You cannot ask an oracle that
|
|
70
|
+
is not installed.
|
|
71
|
+
- **Working an existing `.rigor-baseline.yml` down** →
|
|
72
|
+
`rigor-baseline-reduce`. That is fixing diagnostics, not sourcing a
|
|
73
|
+
type.
|
|
74
|
+
- **Raising type protection from a `coverage --protection` list** →
|
|
75
|
+
`rigor-protection-uplift`. It owns the "where should a type go, and how
|
|
76
|
+
do I verify it" loop; this skill owns "where does the type itself come
|
|
77
|
+
from".
|
|
78
|
+
|
|
79
|
+
## The three allowed sources
|
|
80
|
+
|
|
81
|
+
Every type you write comes from exactly one of these. There is no fourth.
|
|
82
|
+
|
|
83
|
+
| What you need | Ask Rigor with | What you write |
|
|
84
|
+
| --- | --- | --- |
|
|
85
|
+
| The type of an **expression** at a point | `rigor type-of FILE:LINE:COL` — or `rigor annotate FILE` for every line at once | the `type:` it prints, verbatim |
|
|
86
|
+
| The **signature of a method** | `rigor sig-gen --print FILE` | the RBS it prints, verbatim — never what you expected it to print |
|
|
87
|
+
| A **parameter's intended type** | `rigor sig-gen --observe PATH --params=observed` (call-site derivation) | the observed type, *reviewed and widened*, kept only while `rigor check` stays green |
|
|
88
|
+
|
|
89
|
+
The parameter row is the one genuine gap. Inference reads a method
|
|
90
|
+
*body*, and a body does not state what its callers are allowed to pass —
|
|
91
|
+
so Rigor spells parameters `untyped` by design ([ADR-5](https://github.com/rigortype/rigor/blob/master/docs/adr/5-robustness-principle.md):
|
|
92
|
+
strict on returns, lenient on parameters). `--params=observed` derives
|
|
93
|
+
them from the call sites instead, which is evidence rather than
|
|
94
|
+
invention — but it is *narrow* evidence (it can emit literal types such
|
|
95
|
+
as `("JPY")`), so it is the one place you review and widen before
|
|
96
|
+
adopting, under the gate that `rigor check` gains no new diagnostic.
|
|
97
|
+
|
|
98
|
+
Exact command forms, flags, position syntax, JSON shapes, and how to read
|
|
99
|
+
each output: [`references/01-oracle-commands.md`](references/01-oracle-commands.md).
|
|
100
|
+
|
|
101
|
+
## The gap protocol — a gap is a finding, not a blank to fill
|
|
102
|
+
|
|
103
|
+
`Dynamic[top]`, `untyped`, and a `sig.skipped.*` classification are
|
|
104
|
+
**answers**. They mean "Rigor cannot prove a type here", which is
|
|
105
|
+
information about the project or about the engine. Filling that hole with
|
|
106
|
+
your own reading converts a known unknown into a confident falsehood, and
|
|
107
|
+
it does so in a file the next reader will trust.
|
|
108
|
+
|
|
109
|
+
So when the oracle comes back empty:
|
|
110
|
+
|
|
111
|
+
1. **Report it.** Give the exact command and its exact output.
|
|
112
|
+
2. **Find out why**, when it matters: `rigor trace --format=json --line=N FILE`
|
|
113
|
+
replays how the type was built; `rigor explain <rule>` documents a
|
|
114
|
+
diagnostic that fired nearby.
|
|
115
|
+
3. **Route it.** A project-side gap has a sibling skill that closes it
|
|
116
|
+
(missing gem RBS → `rigor-rbs-setup`; an unconfigured framework →
|
|
117
|
+
`rigor-plugin-tune`; the project's own monkey-patches →
|
|
118
|
+
`rigor-monkeypatch-resolve`; a project DSL → `rigor-plugin-author`).
|
|
119
|
+
An engine-side gap is a Rigor issue.
|
|
120
|
+
|
|
121
|
+
Which output means which gap, the full routing table, and how to word the
|
|
122
|
+
issue: [`references/03-gap-protocol.md`](references/03-gap-protocol.md).
|
|
123
|
+
|
|
124
|
+
## Provenance — every type you state carries its command
|
|
125
|
+
|
|
126
|
+
When you tell a human a type, tell them how to re-derive it. One line is
|
|
127
|
+
enough:
|
|
128
|
+
|
|
129
|
+
> `entries_matching` returns `Array[untyped] | []`
|
|
130
|
+
> (`rigor sig-gen --print lib/demo/budget_ledger.rb`).
|
|
131
|
+
|
|
132
|
+
This is not ceremony. It is the difference between an assertion the
|
|
133
|
+
reader must trust and a claim they can re-run in three seconds — and it
|
|
134
|
+
is what makes a wrong answer *findable* instead of permanent.
|
|
135
|
+
|
|
136
|
+
## With the MCP server connected, use the tools
|
|
137
|
+
|
|
138
|
+
If the Rigor MCP server is wired up (`rigor-mcp-setup`), `rigor_type_of`,
|
|
139
|
+
`rigor_annotate`, `rigor_sig_gen`, `rigor_check`, and `rigor_explain` are
|
|
140
|
+
the same oracle as tool calls — prefer them over shelling out, and treat
|
|
141
|
+
their results exactly as this skill treats CLI output. Argument shapes:
|
|
142
|
+
[`references/01-oracle-commands.md`](references/01-oracle-commands.md)
|
|
143
|
+
§ "The MCP tools".
|
|
144
|
+
|
|
145
|
+
## Worked example
|
|
146
|
+
|
|
147
|
+
Asked to document `Demo::BudgetLedger`, the guessing path writes
|
|
148
|
+
`@param currency [String]`, `@return [Numeric]`, `@param pattern
|
|
149
|
+
[Regexp]`. Here is the oracle path.
|
|
150
|
+
|
|
151
|
+
```sh
|
|
152
|
+
rigor annotate lib/demo/budget_ledger.rb
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
def initialize(currency, opening_balance: 0) #=> Dynamic[top]
|
|
157
|
+
@currency = currency #=> Dynamic[top]
|
|
158
|
+
@entries = [] #=> []
|
|
159
|
+
def balance(as_of: nil) #=> Dynamic[top]
|
|
160
|
+
def entries_matching(pattern) #=> Array[Dynamic[top]] | []
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
rigor sig-gen --print lib/demo/budget_ledger.rb
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
rigor sig-gen: skipped 2 method(s) it could not type or would not overwrite
|
|
169
|
+
(sig.skipped.untyped-return: 2). Run with --format=json to see each one with
|
|
170
|
+
its skip_reason.
|
|
171
|
+
class Demo::BudgetLedger
|
|
172
|
+
def initialize: (untyped, ?opening_balance: untyped) -> void
|
|
173
|
+
def record: (untyped, ?memo: untyped, ?at: untyped) -> Demo::BudgetLedger
|
|
174
|
+
def entries_matching: (untyped) -> (Array[untyped] | [])
|
|
175
|
+
end
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
What you now know, and may write: `record` returns
|
|
179
|
+
`Demo::BudgetLedger`; `entries_matching` returns `Array[untyped] | []`.
|
|
180
|
+
What you must **report rather than write**: `balance` and `overdrawn?`
|
|
181
|
+
were skipped as `sig.skipped.untyped-return` — `@opening_balance` is
|
|
182
|
+
`Dynamic[top]`, so the arithmetic proves nothing. `currency` is not
|
|
183
|
+
`String` on any evidence Rigor has; it is `untyped`, and the YARD tag is
|
|
184
|
+
`@param currency — the ledger's currency` with no type at all.
|
|
185
|
+
|
|
186
|
+
Call-site derivation closes the parameter half where specs exist:
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
rigor sig-gen --print --params=observed --observe spec lib/demo/budget_ledger.rb
|
|
190
|
+
# def initialize: ("JPY", ?opening_balance: 100) -> void
|
|
191
|
+
# def entries_matching: (Regexp) -> (Array[untyped] | [])
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`entries_matching: (Regexp)` is now *derived*, and adoptable. The
|
|
195
|
+
`("JPY")` / `100` literals are the narrowness warned about above — widen
|
|
196
|
+
them to `String` / `Integer` before adopting, and keep the change only
|
|
197
|
+
while `rigor check` stays green.
|
|
198
|
+
|
|
199
|
+
## When the user insists on a hand-written type anyway
|
|
200
|
+
|
|
201
|
+
They may. It is their code, and a human can know an intended contract
|
|
202
|
+
that no static reading can prove. Do not argue past one exchange:
|
|
203
|
+
|
|
204
|
+
1. Say once what Rigor actually reports for that site, with the command.
|
|
205
|
+
2. Write what they asked for.
|
|
206
|
+
3. Run `rigor check` on the touched paths and report the result.
|
|
207
|
+
4. **Say which lines the check covers.** A green `rigor check` proves
|
|
208
|
+
that the annotation contradicts nothing Rigor can currently see — it
|
|
209
|
+
does not prove the contract. Where the surrounding types are
|
|
210
|
+
`untyped`, there is nothing to contradict, and you must say so rather
|
|
211
|
+
than let a green run read as confirmation.
|
|
212
|
+
|
|
213
|
+
If the check *does* go red, the annotation modeled the wrong contract:
|
|
214
|
+
report it and revert, never suppress the diagnostic.
|
|
215
|
+
|
|
216
|
+
The same holds for an inline annotation a human wrote as documentation —
|
|
217
|
+
`#: void`, `#: :asc | :desc`, a parameter contract. It is their intent,
|
|
218
|
+
checked by `rigor check`, not a guess for you to "correct" to what inference
|
|
219
|
+
shows. Leave it; if the check finds a contradiction, report that.
|
|
220
|
+
|
|
221
|
+
## Installing the rule in the project
|
|
222
|
+
|
|
223
|
+
An agent that never loads this skill still guesses. The durable fix is
|
|
224
|
+
one paragraph in the project's `AGENTS.md` / `CLAUDE.md`, so the rule is
|
|
225
|
+
in context every session rather than only when a skill happens to
|
|
226
|
+
trigger. The text to paste:
|
|
227
|
+
[`references/02-agents-md-fragment.md`](references/02-agents-md-fragment.md).
|
|
228
|
+
`rigor-project-init` installs it during onboarding.
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# 01 — The oracle commands, exactly
|
|
2
|
+
|
|
3
|
+
Every command form here was taken from `rigor <cmd> --help` on the
|
|
4
|
+
version this file ships with. If a flag below is missing from your
|
|
5
|
+
`--help`, trust `--help`.
|
|
6
|
+
|
|
7
|
+
All five commands are **read-only**: they analyse and print. None of them
|
|
8
|
+
writes to your source tree. (`rigor sig-gen --write` does, and is the one
|
|
9
|
+
form this skill never reaches for on its own — see § "Writing, not just
|
|
10
|
+
reading".)
|
|
11
|
+
|
|
12
|
+
## `rigor type-of` — the type of one expression
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
rigor type-of FILE:LINE[:COL] [FILE:LINE[:COL] ...]
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| Option | Effect |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `--format=text\|json` | `text` (default) or a JSON object per position. |
|
|
21
|
+
| `--trace` | Also record fail-soft fallbacks via the tracer. |
|
|
22
|
+
| `--config=PATH` | Explicit `.rigor.yml`. |
|
|
23
|
+
| `--tmp-file=PATH` / `--instead-of=PATH` | Editor mode: analyse an unsaved buffer as if it were the project path. Paired. |
|
|
24
|
+
|
|
25
|
+
**Position syntax.** `FILE:LINE:COL`, both **1-based**. The column may be
|
|
26
|
+
omitted (`FILE:LINE`). Several positions in one invocation — that is the
|
|
27
|
+
cheap way to ask about a whole method.
|
|
28
|
+
|
|
29
|
+
Text output:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
lib/demo/budget_ledger.rb:19:7
|
|
33
|
+
node: Prism::LocalVariableWriteNode
|
|
34
|
+
type: Array[Dynamic[top]]
|
|
35
|
+
erased: Array[untyped]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- `node` — the Prism node the position resolved to. Check it: if it is
|
|
39
|
+
not the expression you meant, your column is off, and the `type` below
|
|
40
|
+
it answers a different question.
|
|
41
|
+
- `type` — Rigor's internal type. **This is the answer.**
|
|
42
|
+
- `erased` — the same type spelled as RBS. **This is what you write into
|
|
43
|
+
a `.rbs` file or an annotation.** `Dynamic[top]` erases to `untyped`.
|
|
44
|
+
|
|
45
|
+
JSON output is the same five fields:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{ "file": "…", "line": 19, "column": 7,
|
|
49
|
+
"node": "Prism::LocalVariableWriteNode",
|
|
50
|
+
"type": "Array[Dynamic[top]]", "erased": "Array[untyped]" }
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## `rigor annotate` — every line of a file at once
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
rigor annotate FILE
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Option | Effect |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `--format=text\|json` | `text` (default), or JSON as a `{ line => type }` map. |
|
|
62
|
+
| `--[no-]color` | Force / disable ANSI colour (auto-detects a tty; honours `NO_COLOR`). |
|
|
63
|
+
| `--[no-]bat` | Force / disable highlighting through `bat`. |
|
|
64
|
+
| `--config=PATH` | Explicit `.rigor.yml`. |
|
|
65
|
+
|
|
66
|
+
Output is the source with each line's **last-expression type** appended
|
|
67
|
+
after `#=>`:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
def initialize(currency, opening_balance: 0) #=> Dynamic[top]
|
|
71
|
+
@currency = currency #=> Dynamic[top]
|
|
72
|
+
@entries = [] #=> []
|
|
73
|
+
end #=> :initialize
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Two reading rules that catch people out:
|
|
77
|
+
|
|
78
|
+
- `#=>` is the type of the line's **last expression**, not of the
|
|
79
|
+
variable being assigned and not of the method being defined. A `def`
|
|
80
|
+
line's `#=>` is the *return* type of the method; the `end` line's
|
|
81
|
+
`#=>` is the symbol the `def` expression itself evaluates to
|
|
82
|
+
(`:initialize`) — that is Ruby, not a Rigor quirk. Ignore it.
|
|
83
|
+
- One `#=>` per line. For a multi-expression line, or for a
|
|
84
|
+
sub-expression in the middle, go back to `type-of` with a column.
|
|
85
|
+
|
|
86
|
+
Reach for `annotate` first when the task is "document this file" or "add
|
|
87
|
+
types to this class": one call types everything, and the lines that come
|
|
88
|
+
back `Dynamic[top]` are your gap list before you have written a word.
|
|
89
|
+
|
|
90
|
+
## `rigor sig-gen` — the signature of a method
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
rigor sig-gen [paths]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
| Option | Effect |
|
|
97
|
+
| --- | --- |
|
|
98
|
+
| `--print` | RBS to stdout. **Default** — you may omit it. |
|
|
99
|
+
| `--diff` | Unified diff against the existing RBS. Read-only. |
|
|
100
|
+
| `--write` | Write to `sig/<path>.rbs`. The only mode that touches the filesystem. |
|
|
101
|
+
| `--overwrite` | Allow a tighter return to replace user-authored RBS. |
|
|
102
|
+
| `--include-private` | Emit private / protected instance methods too (default: public only). |
|
|
103
|
+
| `--params=untyped\|observed\|observed-strict` | Parameter policy. Default `untyped`. `observed-strict` is reserved and currently a usage error. |
|
|
104
|
+
| `--observe=PATH` | Directory / file to scan for call-site observations. Repeatable. Defaults to `spec/` when present. |
|
|
105
|
+
| `--new-files` / `--new-methods` / `--tighter-returns` | Emit only that classification. |
|
|
106
|
+
| `--format=text\|json` | Text RBS, or the structured candidate report. |
|
|
107
|
+
| `--config=PATH` | Explicit `.rigor.yml`. |
|
|
108
|
+
|
|
109
|
+
Text output is RBS you paste as-is:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
class Demo::BudgetLedger
|
|
113
|
+
# [new]
|
|
114
|
+
def record: (untyped, ?memo: untyped, ?at: untyped) -> Demo::BudgetLedger
|
|
115
|
+
# [new]
|
|
116
|
+
def entries_matching: (untyped) -> (Array[untyped] | [])
|
|
117
|
+
end
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Read stderr.** A skip summary goes there, not to stdout:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
rigor sig-gen: skipped 2 method(s) it could not type or would not overwrite
|
|
124
|
+
(sig.skipped.untyped-return: 2). Run with --format=json to see each one with
|
|
125
|
+
its skip_reason.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
A method that is missing from the printed RBS was **skipped**, and a
|
|
129
|
+
skip is a finding. `--format=json` names each one:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{ "class": "Demo::BudgetLedger", "method": "balance", "kind": "instance",
|
|
133
|
+
"classification": "skipped", "skip_reason": "sig.skipped.untyped-return" }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Classifications: `new-file`, `new-method`, `tighter-return`, `equivalent`
|
|
137
|
+
(nothing to tighten; silently dropped), `skipped`. Skip reasons and what
|
|
138
|
+
each one means for you: [`03-gap-protocol.md`](03-gap-protocol.md).
|
|
139
|
+
|
|
140
|
+
### Deriving a parameter type from call sites
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
rigor sig-gen --print --params=observed --observe spec lib/demo/budget_ledger.rb
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`observed` collects argument types from every call site under the
|
|
147
|
+
`--observe` paths, unions them per parameter position, and emits the
|
|
148
|
+
union. The recogniser understands RSpec shapes — `RSpec.describe Foo`,
|
|
149
|
+
bare `describe Foo`, `subject { … }` / `subject(:name) { … }`,
|
|
150
|
+
`let(:name)` / `let!(:name)`, and `described_class.new(...)` — so a
|
|
151
|
+
normal spec suite is already an observation corpus. No plugin needed.
|
|
152
|
+
|
|
153
|
+
The result is *evidence*, and it is narrow evidence:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
def initialize: ("JPY", ?opening_balance: 100) -> void
|
|
157
|
+
def entries_matching: (Regexp) -> (Array[untyped] | [])
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`(Regexp)` is a real derivation and adoptable. `("JPY")` and `100` are
|
|
161
|
+
literal types — the union of what today's callers happen to pass, frozen
|
|
162
|
+
as a contract. Widen those to the class before adopting
|
|
163
|
+
(`String`, `Integer`), per [ADR-5](https://github.com/rigortype/rigor/blob/master/docs/adr/5-robustness-principle.md):
|
|
164
|
+
lenient on parameters. The gate on any widening is that `rigor check`
|
|
165
|
+
gains no new diagnostic.
|
|
166
|
+
|
|
167
|
+
### Writing, not just reading
|
|
168
|
+
|
|
169
|
+
This skill's job ends at *knowing* the type. `--write` is an edit to the
|
|
170
|
+
project and belongs to whoever asked for it — propose `--diff` first, and
|
|
171
|
+
run `rigor check` after. `--write` only ever writes inside the configured
|
|
172
|
+
signature paths (`sig/` by default).
|
|
173
|
+
|
|
174
|
+
## `rigor trace` — why the type is what it is
|
|
175
|
+
|
|
176
|
+
```sh
|
|
177
|
+
rigor trace FILE
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
| Option | Effect |
|
|
181
|
+
| --- | --- |
|
|
182
|
+
| `--format=text\|json` | `text` is an interactive animation; **use `json`** for the raw event stream. |
|
|
183
|
+
| `--line=N` | Only replay events whose source range starts on line `N`. |
|
|
184
|
+
| `--verbose` | Include every expression enter/result frame. |
|
|
185
|
+
| `--delay=SECONDS` | Autoplay the text animation (default: step on key press). |
|
|
186
|
+
| `--config=PATH` | Explicit `.rigor.yml`. |
|
|
187
|
+
|
|
188
|
+
An agent wants `--format=json --line=N`: the text mode waits on
|
|
189
|
+
keystrokes. Each event carries `kind`, `depth`, a `location`, the node
|
|
190
|
+
`stack`, and a `data` payload — a `bind` event, for example, names the
|
|
191
|
+
local and the type it received:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{ "kind": "bind", "depth": 2,
|
|
195
|
+
"location": { "start_line": 20, "start_column": 25, "…": "…" },
|
|
196
|
+
"stack": ["CallNode", "CallNode"],
|
|
197
|
+
"data": { "name": "amount", "type": "Dynamic[top]" } }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Reach for it when `type-of` gave you `Dynamic[top]` and you need to name
|
|
201
|
+
*where* the precision was lost before reporting the gap.
|
|
202
|
+
|
|
203
|
+
## `rigor explain` — what a diagnostic rule means
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
rigor explain [<rule>]
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
| Option | Effect |
|
|
210
|
+
| --- | --- |
|
|
211
|
+
| `--format=text\|json` | Default `text`. |
|
|
212
|
+
|
|
213
|
+
With no argument it lists every rule. With a rule id, a legacy alias, or
|
|
214
|
+
a family prefix (`call`, `flow`, `assert`, `dump`, `def`) it prints the
|
|
215
|
+
rule's firing conditions, the severity per profile, the evidence tier,
|
|
216
|
+
and how to suppress it.
|
|
217
|
+
|
|
218
|
+
**`explain` covers diagnostic rules only.** A `sig.skipped.*` id is a
|
|
219
|
+
sig-gen *skip reason*, not a diagnostic rule — `rigor explain
|
|
220
|
+
sig.skipped.untyped-return` answers `Unknown rule`. Skip reasons are
|
|
221
|
+
documented in [`03-gap-protocol.md`](03-gap-protocol.md).
|
|
222
|
+
|
|
223
|
+
## `rigor check` — the gate, not the oracle
|
|
224
|
+
|
|
225
|
+
```sh
|
|
226
|
+
rigor check [paths]
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
You need one flag family here: `--format=text|json|sarif|github|gitlab|checkstyle|junit|teamcity`,
|
|
230
|
+
plus `--explain` to surface fail-soft fallback events as `:info`
|
|
231
|
+
diagnostics. Everything else (`--baseline*`, `--workers`, `--incremental`,
|
|
232
|
+
the cache flags) belongs to other skills.
|
|
233
|
+
|
|
234
|
+
`check` never *sources* a type. Its role here is the verification half:
|
|
235
|
+
after any annotation lands, `rigor check PATHS` must gain **no new
|
|
236
|
+
diagnostic** versus before. Green is a non-contradiction proof, not a
|
|
237
|
+
correctness proof — say which lines it actually covers.
|
|
238
|
+
|
|
239
|
+
## The MCP tools
|
|
240
|
+
|
|
241
|
+
With the Rigor MCP server wired up (`rigor-mcp-setup`), the same oracle
|
|
242
|
+
is available as tool calls. Prefer them over shelling out; the results
|
|
243
|
+
are the CLI's, so read them exactly as above.
|
|
244
|
+
|
|
245
|
+
| Tool | Required arguments | Optional | Returns |
|
|
246
|
+
| --- | --- | --- | --- |
|
|
247
|
+
| `rigor_type_of` | `file` (string), `line` (integer, 1-based), `col` (integer, 1-based) | `config` | The inferred type at that location. |
|
|
248
|
+
| `rigor_annotate` | `file` (string) | `config` | The source with each line's last-expression type appended. |
|
|
249
|
+
| `rigor_sig_gen` | — (defaults to the configured paths) | `paths` (array of strings), `params` (`"untyped"` \| `"observed"`), `config` | The JSON candidate report — classifications, `rbs`, `inferred_return`, `skip_reason`. |
|
|
250
|
+
| `rigor_check` | `paths` (array of strings) | `config` | The JSON diagnostic report. |
|
|
251
|
+
| `rigor_explain` | — (omit `rule` to list every rule) | `rule` (string) | The rule catalogue entry, as JSON. |
|
|
252
|
+
|
|
253
|
+
Two shape differences from the CLI worth knowing:
|
|
254
|
+
|
|
255
|
+
- `rigor_sig_gen` returns the **JSON candidate report**, always — there
|
|
256
|
+
is no `--print` text mode and no `--diff`. Read `rbs` per candidate.
|
|
257
|
+
- `rigor_sig_gen` exposes `params` but **not** `observe`; observation
|
|
258
|
+
falls back to `spec/` when present. Point it elsewhere from the CLI.
|
|
259
|
+
|
|
260
|
+
`rigor_triage` and `rigor_coverage` are also served, and belong to
|
|
261
|
+
`rigor-baseline-reduce` / `rigor-protection-uplift` rather than here.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# 02 — The paragraph to keep in the project's agent contract
|
|
2
|
+
|
|
3
|
+
A skill only binds an agent that loaded it. The rule below binds every
|
|
4
|
+
session, because it lives in the file the agent reads at startup —
|
|
5
|
+
`AGENTS.md`, or `CLAUDE.md` for Claude Code (which pulls `AGENTS.md` in
|
|
6
|
+
with `@AGENTS.md` when both exist).
|
|
7
|
+
|
|
8
|
+
Paste it verbatim. `rigor-project-init` installs it during onboarding; if
|
|
9
|
+
the project is already onboarded, add it now.
|
|
10
|
+
|
|
11
|
+
## Where it goes
|
|
12
|
+
|
|
13
|
+
- **`AGENTS.md` exists** → append the section at the end. Do not
|
|
14
|
+
reorganise the file.
|
|
15
|
+
- **Only `CLAUDE.md` exists** → append it there.
|
|
16
|
+
- **Both exist** → put it in `AGENTS.md`; add `@AGENTS.md` to `CLAUDE.md`
|
|
17
|
+
only if it is not already pulled in.
|
|
18
|
+
- **Neither exists** → create `AGENTS.md` with this as its first section.
|
|
19
|
+
- **A "Types" / "Type checking" section already exists** → merge into it
|
|
20
|
+
rather than adding a second one, and keep the five bullets intact.
|
|
21
|
+
|
|
22
|
+
Never overwrite unrelated content, and never rewrite a rule the project
|
|
23
|
+
already wrote for itself — show the user the conflict instead.
|
|
24
|
+
|
|
25
|
+
## The fragment
|
|
26
|
+
|
|
27
|
+
```markdown
|
|
28
|
+
## Types come from Rigor, not from reading code
|
|
29
|
+
|
|
30
|
+
This project is type-checked by [Rigor](https://github.com/rigortype/rigor). A type you did not obtain from Rigor is a guess, and a guessed type is never written anywhere: not in `sig/`, not in an inline annotation (`#:`, `# @rbs`), not in a doc comment, not in a review comment, and not as the reason for a nil check or an `is_a?` guard.
|
|
31
|
+
|
|
32
|
+
- The type of an expression: `rigor type-of FILE:LINE:COL`, or `rigor annotate FILE` for a whole file.
|
|
33
|
+
- The signature of a method: `rigor sig-gen --print FILE`; paste what it prints, never what you expect.
|
|
34
|
+
- A parameter type is the one thing inference does not give you: derive it from the call sites with `rigor sig-gen --observe PATH` and keep it only while `rigor check` stays green.
|
|
35
|
+
- When Rigor answers `Dynamic[top]` or `untyped`, or `sig-gen` skips the method, do not fill the gap. Report the exact command and its output; the gap is the finding.
|
|
36
|
+
- Every type you state to a human carries the command that produced it, so it can be re-run.
|
|
37
|
+
|
|
38
|
+
The `rigor-type-oracle` skill (`rigor skill --full rigor-type-oracle`) has the full procedure. With the Rigor MCP server connected, `rigor_type_of`, `rigor_annotate`, and `rigor_sig_gen` are the same oracle as tool calls.
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Two notes for whoever installs it
|
|
42
|
+
|
|
43
|
+
- **The single-long-line shape is deliberate.** Each bullet is one line,
|
|
44
|
+
however long. Some of these files are rendered as Markdown by tools
|
|
45
|
+
that turn a newline inside a paragraph into a line break, so a
|
|
46
|
+
column-wrapped bullet reads ragged. Do not re-wrap it to fit an editor
|
|
47
|
+
ruler.
|
|
48
|
+
- **Tell the user it landed.** It is a change to the file that governs
|
|
49
|
+
every future agent session in the repo — it belongs in the "here is
|
|
50
|
+
what I created, and whether to commit it" report, not in a silent diff.
|
|
51
|
+
It should be committed: the point is that every contributor's agent
|
|
52
|
+
reads the same rule.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# 03 — The gap protocol
|
|
2
|
+
|
|
3
|
+
Rigor answering "I don't know" is not the oracle failing. It is the
|
|
4
|
+
oracle telling you something the code alone would not have: precision is
|
|
5
|
+
lost *here*, for *this* reason. That is worth more than the annotation
|
|
6
|
+
you were about to write, and it is worth exactly nothing if you paper
|
|
7
|
+
over it.
|
|
8
|
+
|
|
9
|
+
**The rule: never convert a gap into a type.** Report it, locate it,
|
|
10
|
+
route it.
|
|
11
|
+
|
|
12
|
+
## Which output means which gap
|
|
13
|
+
|
|
14
|
+
### `Dynamic[top]` (erased: `untyped`)
|
|
15
|
+
|
|
16
|
+
Rigor has no class information for this expression at all. It is the
|
|
17
|
+
top type, and it is contagious: a `Dynamic[top]` receiver makes every
|
|
18
|
+
method call on it `Dynamic[top]` too, so one unresolved source can
|
|
19
|
+
account for a whole file of "unknown".
|
|
20
|
+
|
|
21
|
+
Common causes, in the order worth checking:
|
|
22
|
+
|
|
23
|
+
- A **method parameter** — parameters are `untyped` by design unless RBS
|
|
24
|
+
or observation says otherwise. Not a defect; use call-site derivation.
|
|
25
|
+
- An **ivar** assigned from a parameter (`@currency = currency`) — the
|
|
26
|
+
same gap, one hop later.
|
|
27
|
+
- A call into a **gem with no RBS** — the dominant cause on a real
|
|
28
|
+
project.
|
|
29
|
+
- A call into a **framework Rigor is not configured for** — Rails
|
|
30
|
+
without the Rails plugins enabled, say.
|
|
31
|
+
- The project's **own monkey-patch or DSL**, which the analyzer never saw
|
|
32
|
+
defined.
|
|
33
|
+
|
|
34
|
+
### `untyped` inside an otherwise-precise type
|
|
35
|
+
|
|
36
|
+
`Array[untyped]`, `Hash[Symbol, untyped]`: the container is proven, the
|
|
37
|
+
element is not. Write the container. Do **not** invent the element — an
|
|
38
|
+
`Array[String]` you guessed is worse than the `Array[untyped]` Rigor
|
|
39
|
+
proved, because it will be believed.
|
|
40
|
+
|
|
41
|
+
### A method missing from `sig-gen` output — the `sig.skipped.*` reasons
|
|
42
|
+
|
|
43
|
+
Read stderr for the count, `--format=json` for the per-method reason.
|
|
44
|
+
|
|
45
|
+
| Reason | What it means | What you do |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| `sig.skipped.untyped-return` | The body's last expression types as `Dynamic[top]`; emitting `untyped` would be noise. | The commonest gap. Trace the return expression back to its `Dynamic[top]` source and route *that*. Never write a return type here. |
|
|
48
|
+
| `sig.skipped.user-authored` | An RBS declaration already exists and `--overwrite` was not given. | Not a gap. The hand-written type is the project's answer — read `sig/`, and if it disagrees with inference, raise the disagreement rather than silently retyping. |
|
|
49
|
+
| `sig.skipped.unrenderable-rbs` | Rigor rendered a signature that does not parse as RBS, so it was dropped rather than written. | **A bug in Rigor**, not in the code. Report it with the method and the file. |
|
|
50
|
+
| `sig.skipped.complex-shape` | Reserved; the generator does not produce it today. | If you ever see one, it is worth reporting as a surprise. |
|
|
51
|
+
| `skipped_outside_sig_root` | A `--write` target outside the configured signature paths. | Configuration, not inference. |
|
|
52
|
+
|
|
53
|
+
`rigor explain <id>` answers each of these ids directly (the command
|
|
54
|
+
carries a second catalogue for the skip reasons alongside the
|
|
55
|
+
diagnostic rules); this table is the summary.
|
|
56
|
+
|
|
57
|
+
### `equivalent`, and silence
|
|
58
|
+
|
|
59
|
+
A `tighter-return` candidate that never appears was classified
|
|
60
|
+
`equivalent`: the inferred return is not a strict subtype of what `sig/`
|
|
61
|
+
already declares. The existing declaration stands. Silence here means
|
|
62
|
+
"nothing to change", not "nothing is known" — check `sig/` before calling
|
|
63
|
+
it a gap.
|
|
64
|
+
|
|
65
|
+
## Locating the gap before you report it
|
|
66
|
+
|
|
67
|
+
Two commands turn "somewhere upstream" into a line number.
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
rigor annotate FILE # walk the #=> column upward to the first Dynamic[top]
|
|
71
|
+
rigor trace --format=json --line=N FILE
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`annotate` is usually enough: the first line in the chain that reports
|
|
75
|
+
`Dynamic[top]` is the source, and everything below it is cascade. Use
|
|
76
|
+
`trace` when the loss happens *within* a line — its `bind` events name
|
|
77
|
+
each local and the type it received, so you can see which argument
|
|
78
|
+
arrived unknown.
|
|
79
|
+
|
|
80
|
+
If a diagnostic fired near the site, `rigor explain <rule>` gives its
|
|
81
|
+
firing conditions, which frequently name the cause outright.
|
|
82
|
+
|
|
83
|
+
## Routing table — project-side gaps have an owner
|
|
84
|
+
|
|
85
|
+
Once you know the source, the fix is almost never "write the type here".
|
|
86
|
+
|
|
87
|
+
| The gap's source | Route to | Why |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| A dependency gem ships no RBS | **`rigor-rbs-setup`** | `rbs collection install` brings in community RBS; this is the single biggest `Dynamic` reduction on most projects. |
|
|
90
|
+
| A framework Rigor isn't configured for (Rails, RSpec, dry-rb…) | **`rigor-plugin-tune`** | The bundled plugin for it is probably just not enabled in `.rigor.dist.yml`. |
|
|
91
|
+
| `undefined-method` on the project's own monkey-patches | **`rigor-monkeypatch-resolve`** | Wiring the defining files into `pre_eval:` makes them visible. |
|
|
92
|
+
| The project's own DSL / `define_method` factory / `method_missing` | **`rigor-plugin-author`** | Rigor does not bundle per-application plugins; a project-owned plugin is the durable fix. |
|
|
93
|
+
| The site is protected-coverage work, not a documentation task | **`rigor-protection-uplift`** | It owns the "minimal true annotation + double gate" loop. |
|
|
94
|
+
| The setup itself looks wrong (zero RBS classes, config not taking) | **`rigor-doctor`** | Validate before concluding anything about inference. |
|
|
95
|
+
| None of the above — Rigor should have inferred this | A Rigor issue | See below. |
|
|
96
|
+
|
|
97
|
+
Offer the route; do not silently switch tasks. The user asked you to
|
|
98
|
+
document a class, and "the reason I cannot is X, and here is the skill
|
|
99
|
+
that fixes X" is the answer to that request.
|
|
100
|
+
|
|
101
|
+
## Reporting an engine-side gap
|
|
102
|
+
|
|
103
|
+
When the type is derivable from the source and Rigor still says
|
|
104
|
+
`Dynamic[top]`, that is a completeness gap in the engine, and it is worth
|
|
105
|
+
more to the project than any annotation. File it at
|
|
106
|
+
<https://github.com/rigortype/rigor/issues> with these five parts:
|
|
107
|
+
|
|
108
|
+
1. **The file and line** — the smallest reproduction you can get to,
|
|
109
|
+
ideally a standalone snippet rather than a pointer into a private
|
|
110
|
+
repo.
|
|
111
|
+
2. **The exact command**, as run: `rigor type-of demo.rb:12:5`,
|
|
112
|
+
`rigor sig-gen --print demo.rb`.
|
|
113
|
+
3. **The exact output**, pasted, including the stderr skip line.
|
|
114
|
+
4. **What the correct type is, and what proves it** — the Ruby-level
|
|
115
|
+
reasoning a reader can verify. "`Array#sum` over `Integer` elements is
|
|
116
|
+
`Integer`", not "it should obviously be Integer".
|
|
117
|
+
5. **What the report proves** — one sentence naming the *class* of gap,
|
|
118
|
+
not just the instance: "block-parameter destructuring loses the
|
|
119
|
+
element type", "a keyword default of `Time.now` does not seed the
|
|
120
|
+
parameter". This is the part that makes the issue actionable, because
|
|
121
|
+
it says what a fix would generalise to.
|
|
122
|
+
|
|
123
|
+
Also say `rigor --version`, and note whether `sig/` and the relevant
|
|
124
|
+
plugins were in play — a gap that only appears without community RBS is a
|
|
125
|
+
different bug from one that survives it.
|
|
126
|
+
|
|
127
|
+
Inside Rigor's own tree the same report is the deliverable: the gap is
|
|
128
|
+
the reason not to hand-write the RBS there.
|