rigortype 0.3.8 → 0.4.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/capability_roles/capability_roles.rbs +36 -0
- data/data/core_overlay/enumerable.rbs +51 -0
- data/data/core_overlay/enumerator.rbs +84 -0
- data/data/core_overlay/hash_rbs3.rbs +41 -0
- data/data/core_overlay/process.rbs +40 -0
- data/data/core_overlay/string_io.rbs +33 -0
- data/data/effects/core.yml +3 -3
- data/data/gem_overlay/activesupport/core_ext.rbs +236 -17
- data/docs/handbook/02-everyday-types.md +17 -14
- data/docs/handbook/03-narrowing.md +132 -14
- data/docs/handbook/04-tuples-and-shapes.md +8 -6
- 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 +19 -1
- data/docs/handbook/10-sorbet.md +9 -10
- data/docs/handbook/11-sig-gen.md +525 -13
- 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 +95 -6
- data/docs/manual/03-configuration.md +18 -0
- data/docs/manual/04-diagnostics.md +15 -0
- data/docs/manual/07-plugins.md +15 -5
- data/docs/manual/08-skills.md +23 -2
- data/docs/manual/10-mcp-server.md +3 -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 +46 -8
- data/docs/manual/18-removing-dead-code.md +10 -8
- data/docs/manual/19-effect-labels.md +10 -0
- data/docs/manual/plugins/README.md +7 -0
- data/docs/manual/plugins/rigor-actioncable.md +8 -1
- data/docs/manual/plugins/rigor-actionmailer.md +7 -0
- data/docs/manual/plugins/rigor-actionpack.md +261 -0
- data/docs/manual/plugins/rigor-active-model-serializers.md +153 -0
- data/docs/manual/plugins/rigor-activejob.md +7 -0
- data/docs/manual/plugins/rigor-activerecord.md +138 -4
- data/docs/manual/plugins/rigor-activestorage.md +8 -1
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +43 -7
- data/docs/manual/plugins/rigor-grape.md +106 -0
- data/docs/manual/plugins/rigor-graphql.md +23 -2
- data/docs/manual/plugins/rigor-pundit.md +8 -1
- data/docs/manual/plugins/rigor-rails-i18n.md +10 -5
- data/docs/manual/plugins/rigor-rails-routes.md +7 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +229 -1
- data/docs/manual/plugins/rigor-sidekiq.md +8 -1
- data/docs/manual/plugins/rigor-sorbet.md +21 -5
- data/exe/rigor +19 -4
- data/lib/rigor/analysis/baseline.rb +3 -3
- 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/declaration_sourced_guard.rb +167 -2
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +10 -2
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +189 -0
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +6 -4
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +15 -6
- data/lib/rigor/analysis/check_rules/rule_ids.rb +21 -6
- data/lib/rigor/analysis/check_rules/rule_walk.rb +41 -5
- 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/source_arity.rb +323 -0
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +146 -0
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +38 -4
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +0 -1
- data/lib/rigor/analysis/check_rules.rb +718 -79
- data/lib/rigor/analysis/crash_signature.rb +8 -24
- data/lib/rigor/analysis/dependency_recorder.rb +23 -0
- 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 +15 -1
- data/lib/rigor/analysis/incremental.rb +42 -0
- data/lib/rigor/analysis/incremental_session.rb +117 -26
- data/lib/rigor/analysis/plugin_fact_fingerprint.rb +0 -1
- data/lib/rigor/analysis/project_scan.rb +11 -1
- data/lib/rigor/analysis/reachability/graph.rb +3 -5
- data/lib/rigor/analysis/reachability/plugin_roots.rb +5 -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 +3 -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 +131 -1
- data/lib/rigor/analysis/run_cache_key.rb +21 -12
- 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 +340 -33
- data/lib/rigor/analysis/runner/effect_annotation_residual_pass.rb +2 -3
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +18 -14
- data/lib/rigor/analysis/runner/pool_coordinator.rb +357 -97
- data/lib/rigor/analysis/runner/project_pre_passes.rb +38 -13
- data/lib/rigor/analysis/runner/run_snapshots.rb +6 -1
- data/lib/rigor/analysis/runner.rb +407 -51
- data/lib/rigor/analysis/template_unit_collector.rb +303 -0
- data/lib/rigor/analysis/template_unit_paths.rb +91 -0
- data/lib/rigor/analysis/template_unit_positions.rb +293 -0
- data/lib/rigor/analysis/template_units.rb +399 -0
- data/lib/rigor/analysis/worker_session.rb +62 -20
- data/lib/rigor/bleeding_edge.rb +10 -24
- data/lib/rigor/builtins/hkt_builtins.rb +3 -2
- data/lib/rigor/builtins/imported_refinements.rb +159 -21
- data/lib/rigor/builtins/predefined_constant_refinements.rb +5 -7
- data/lib/rigor/builtins/regex_refinement.rb +21 -13
- data/lib/rigor/builtins/static_return_refinements.rb +7 -6
- data/lib/rigor/cache/annotation_location.rb +2 -4
- data/lib/rigor/cache/descriptor.rb +63 -20
- data/lib/rigor/cache/engine_source.rb +33 -5
- data/lib/rigor/cache/file_digest.rb +9 -1
- data/lib/rigor/cache/incremental_snapshot.rb +72 -6
- data/lib/rigor/cache/rbs_cache_producer.rb +4 -0
- 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 +112 -14
- 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 +76 -32
- data/lib/rigor/ci_detector.rb +1 -0
- 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 +4 -4
- data/lib/rigor/cli/docs_command.rb +6 -6
- 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 +4 -4
- data/lib/rigor/cli/plugins_command.rb +2 -1
- data/lib/rigor/cli/prism_colorizer.rb +1 -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 +212 -31
- data/lib/rigor/cli/skill_command.rb +2 -2
- data/lib/rigor/cli/skill_deep_probe.rb +4 -4
- data/lib/rigor/cli/skill_describe.rb +35 -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 +29 -11
- data/lib/rigor/cli/type_of_renderer.rb +20 -9
- data/lib/rigor/cli/type_of_template_probe.rb +189 -0
- 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 +89 -9
- data/lib/rigor/config_audit.rb +30 -5
- data/lib/rigor/configuration/severity_profile.rb +25 -9
- data/lib/rigor/configuration.rb +109 -7
- data/lib/rigor/effects/ancestry_recorder.rb +191 -0
- data/lib/rigor/effects/attribution.rb +12 -5
- data/lib/rigor/effects/callee_rule.rb +368 -0
- data/lib/rigor/effects/catalog.rb +7 -4
- data/lib/rigor/effects/collector.rb +11 -5
- data/lib/rigor/effects/config_envelopes.rb +15 -10
- data/lib/rigor/effects/definition_context.rb +179 -0
- data/lib/rigor/effects/definition_lines.rb +25 -6
- data/lib/rigor/effects/effect_table.rb +11 -4
- data/lib/rigor/effects/entry_points.rb +0 -2
- data/lib/rigor/effects/envelope_check.rb +9 -9
- data/lib/rigor/effects/envelope_index.rb +20 -8
- data/lib/rigor/effects/file_collection.rb +59 -5
- data/lib/rigor/effects/framework_units.rb +5 -7
- data/lib/rigor/effects/identity.rb +20 -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/local_ownership.rb +38 -12
- data/lib/rigor/effects/method_key.rb +22 -1
- data/lib/rigor/effects/mutation_classifier.rb +23 -12
- data/lib/rigor/effects/plugin_facts.rb +47 -37
- data/lib/rigor/effects/propagator.rb +297 -19
- data/lib/rigor/effects/registry.rb +1 -1
- data/lib/rigor/effects/scanner.rb +122 -73
- data/lib/rigor/effects/signature_sources.rb +4 -6
- data/lib/rigor/effects/snapshot.rb +12 -11
- data/lib/rigor/effects/snapshot_diff.rb +1 -1
- data/lib/rigor/effects/summary.rb +27 -4
- data/lib/rigor/effects/unit_scan.rb +393 -38
- data/lib/rigor/effects/unknown_label_check.rb +3 -8
- data/lib/rigor/effects/unknown_label_report.rb +3 -4
- data/lib/rigor/effects/visibility.rb +101 -0
- 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 +68 -4
- data/lib/rigor/environment/member_consistency/comparator.rb +708 -0
- data/lib/rigor/environment/member_consistency.rb +298 -0
- 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 +674 -79
- data/lib/rigor/environment.rb +174 -24
- data/lib/rigor/flow_contribution/merger.rb +0 -2
- data/lib/rigor/flow_contribution.rb +11 -13
- data/lib/rigor/hashing/xxh3.rb +264 -0
- data/lib/rigor/inference/acceptance.rb +292 -17
- data/lib/rigor/inference/block_auto_splat.rb +216 -0
- data/lib/rigor/inference/block_call_timing.rb +338 -0
- data/lib/rigor/inference/block_parameter_binder.rb +75 -30
- data/lib/rigor/inference/block_repetition.rb +71 -0
- data/lib/rigor/inference/body_fixpoint.rb +7 -6
- data/lib/rigor/inference/budget_trace.rb +3 -6
- data/lib/rigor/inference/builtins/method_catalog.rb +2 -1
- data/lib/rigor/inference/builtins/string_catalog.rb +1 -1
- data/lib/rigor/inference/captured_locals.rb +389 -18
- data/lib/rigor/inference/closure_escape_analyzer.rb +159 -17
- data/lib/rigor/inference/content_join.rb +200 -27
- data/lib/rigor/inference/coverage_scanner.rb +4 -6
- data/lib/rigor/inference/def_return_typer.rb +11 -7
- data/lib/rigor/inference/define_method_block_self.rb +64 -0
- data/lib/rigor/inference/dynamic_origin.rb +1 -1
- data/lib/rigor/inference/element_read_widening.rb +180 -0
- data/lib/rigor/inference/error_info.rb +196 -0
- data/lib/rigor/inference/expression_typer.rb +2124 -468
- data/lib/rigor/inference/external_ancestor_resolution.rb +267 -0
- data/lib/rigor/inference/fork_map.rb +5 -7
- data/lib/rigor/inference/fresh_frame_blocks.rb +127 -0
- data/lib/rigor/inference/global_write_census.rb +239 -0
- data/lib/rigor/inference/guard_rebinding.rb +447 -0
- data/lib/rigor/inference/hash_lookup_mutation.rb +88 -0
- 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 +16 -7
- data/lib/rigor/inference/indexed_narrowing.rb +61 -9
- data/lib/rigor/inference/jump_targets.rb +82 -0
- data/lib/rigor/inference/last_line/implicit_self.rb +210 -0
- data/lib/rigor/inference/last_line/self_evidence.rb +329 -0
- data/lib/rigor/inference/last_line.rb +340 -0
- data/lib/rigor/inference/last_status.rb +144 -0
- data/lib/rigor/inference/macro_block_self_type.rb +168 -21
- data/lib/rigor/inference/match_rebinding/calls.rb +281 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +148 -0
- data/lib/rigor/inference/match_rebinding/operands.rb +236 -0
- data/lib/rigor/inference/match_rebinding/self_calls.rb +83 -0
- data/lib/rigor/inference/match_rebinding.rb +392 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +36 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +87 -33
- 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/facet_distribution.rb +147 -0
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/hash_transform_keys_folding.rb +191 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +6 -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/match_data_folding.rb +159 -0
- 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 +165 -111
- data/lib/rigor/inference/method_dispatcher/process_folding.rb +56 -0
- data/lib/rigor/inference/method_dispatcher/proven_overload.rb +72 -0
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +82 -0
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +1046 -69
- 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/self_substitute.rb +142 -0
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +129 -54
- 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 +5 -6
- 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 +225 -25
- data/lib/rigor/inference/method_parameter_binder.rb +12 -9
- data/lib/rigor/inference/multi_target_binder.rb +341 -55
- data/lib/rigor/inference/mutation_rejoin.rb +171 -0
- data/lib/rigor/inference/mutation_widening.rb +156 -85
- data/lib/rigor/inference/narrowing.rb +835 -162
- data/lib/rigor/inference/operand_effects.rb +167 -0
- data/lib/rigor/inference/operand_walk.rb +88 -0
- data/lib/rigor/inference/optimistic_origin.rb +153 -16
- data/lib/rigor/inference/origin_lookup.rb +2 -3
- data/lib/rigor/inference/parameter_inference_collector.rb +7 -7
- data/lib/rigor/inference/pre_eval_constants.rb +6 -5
- data/lib/rigor/inference/precision_scanner.rb +3 -4
- data/lib/rigor/inference/project_method_ownership.rb +136 -0
- data/lib/rigor/inference/project_patched_methods.rb +9 -4
- data/lib/rigor/inference/project_patched_scanner.rb +10 -6
- 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 +93 -4
- data/lib/rigor/inference/receiver_blind_block.rb +219 -0
- data/lib/rigor/inference/refinement_mutation.rb +76 -0
- data/lib/rigor/inference/repeated_or_writes.rb +463 -0
- data/lib/rigor/inference/return_barrier.rb +54 -0
- data/lib/rigor/inference/rewrite_mutation.rb +120 -0
- data/lib/rigor/inference/scope_indexer.rb +5228 -627
- data/lib/rigor/inference/statement_evaluator.rb +3452 -595
- data/lib/rigor/inference/stored_block_call.rb +54 -0
- data/lib/rigor/inference/string_mutation.rb +97 -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/unknown_store_widening.rb +200 -0
- data/lib/rigor/inference/unthreaded_rebinds.rb +282 -0
- 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 +0 -1
- data/lib/rigor/language_server/diagnostic_publisher.rb +7 -6
- 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 +4 -4
- data/lib/rigor/language_server/incremental_sync.rb +6 -6
- data/lib/rigor/language_server/project_context.rb +7 -8
- 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/mcp/server.rb +2 -1
- data/lib/rigor/plugin/base.rb +258 -26
- data/lib/rigor/plugin/box_probe.rb +91 -0
- data/lib/rigor/plugin/bundled_catalog.rb +167 -0
- data/lib/rigor/plugin/effect_attribution.rb +65 -11
- 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 +33 -13
- data/lib/rigor/plugin/macro/block_as_method.rb +45 -6
- 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 +94 -11
- data/lib/rigor/plugin/registry.rb +76 -12
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +21 -4
- data/lib/rigor/plugin/template_unit.rb +196 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -4
- data/lib/rigor/plugin/type_node_resolver.rb +3 -3
- data/lib/rigor/plugin.rb +1 -0
- 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 +14 -12
- data/lib/rigor/protection/kill_signature.rb +1 -4
- data/lib/rigor/protection/measurement_integrity.rb +1 -3
- data/lib/rigor/protection/mutation_cache.rb +7 -11
- 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 +88 -21
- data/lib/rigor/reflection/constant_ancestors.rb +97 -0
- data/lib/rigor/reflection/constant_path.rb +139 -0
- data/lib/rigor/reflection.rb +93 -110
- data/lib/rigor/runtime/jit.rb +6 -8
- data/lib/rigor/scope/discovery_index.rb +165 -2
- data/lib/rigor/scope.rb +1041 -32
- data/lib/rigor/sig_gen/alias_index.rb +289 -0
- data/lib/rigor/sig_gen/classification.rb +22 -5
- data/lib/rigor/sig_gen/declaration_equivalence.rb +127 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +202 -0
- data/lib/rigor/sig_gen/generator.rb +673 -53
- data/lib/rigor/sig_gen/inline_declarations.rb +184 -0
- 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/method_candidate.rb +47 -3
- data/lib/rigor/sig_gen/observation_collector.rb +18 -16
- 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 +128 -14
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +154 -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/write_result.rb +19 -3
- data/lib/rigor/sig_gen/writer.rb +189 -41
- data/lib/rigor/sig_gen.rb +3 -0
- 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 +0 -6
- data/lib/rigor/source/parameter_envelope.rb +72 -0
- data/lib/rigor/source.rb +1 -0
- 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 +144 -5
- data/lib/rigor/type/data_class.rb +2 -2
- data/lib/rigor/type/data_instance.rb +4 -4
- data/lib/rigor/type/difference.rb +1 -0
- data/lib/rigor/type/float_range.rb +128 -0
- data/lib/rigor/type/hash_shape.rb +6 -6
- data/lib/rigor/type/integer_range.rb +13 -8
- data/lib/rigor/type/nominal.rb +8 -2
- data/lib/rigor/type/refined.rb +4 -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/lib/rigor.rb +1 -0
- 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/effects.rb +1 -0
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +10 -5
- 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 +14 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +32 -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/controller_scan.rb +96 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/effects.rb +65 -8
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +270 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/render_locals.rb +402 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +370 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_units.rb +132 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +264 -3
- data/plugins/rigor-actionpack/sig/action_controller.rbs +71 -0
- data/plugins/rigor-actionpack/sig/action_view.rbs +43 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_discoverer.rb +220 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers/serializer_index.rb +56 -0
- data/plugins/rigor-active-model-serializers/lib/rigor/plugin/active_model_serializers.rb +263 -0
- data/plugins/rigor-active-model-serializers/lib/rigor-active-model-serializers.rb +7 -0
- data/plugins/rigor-active-model-serializers/sig/active_model_serializers.rbs +45 -0
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +0 -4
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +2 -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 +10 -5
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +35 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +72 -12
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +383 -7
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +14 -6
- 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 +322 -41
- data/plugins/rigor-activerecord/sig/active_record/framework.rbs +123 -0
- data/plugins/rigor-activerecord/sig/active_record/relation.rbs +206 -31
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +26 -14
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +9 -2
- 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 +290 -19
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +1 -0
- data/plugins/rigor-dry-monads/lib/rigor/plugin/dry_monads.rb +3 -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 +2 -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/types.rb +1 -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-grape/lib/rigor/plugin/grape.rb +141 -0
- data/plugins/rigor-grape/lib/rigor-grape.rb +6 -0
- data/plugins/rigor-grape/sig/grape.rbs +263 -0
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +8 -6
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +78 -5
- data/plugins/rigor-graphql/sig/graphql.rbs +485 -0
- 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 +2 -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 +12 -7
- 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 +36 -37
- 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 +0 -10
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +5 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/doorkeeper_routes.rb +3 -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 +10 -17
- 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/same_line_annotations.rb +149 -0
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +324 -18
- 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 +2 -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 +10 -5
- 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 +3 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +7 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +51 -6
- data/plugins/rigor-sorbet/sig/sorbet.rbs +277 -0
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +1 -0
- data/sig/rigor/analysis/baseline.rbs +69 -7
- data/sig/rigor/analysis/fact_store.rbs +1 -1
- data/sig/rigor/analysis/project_scan.rbs +74 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +12 -0
- data/sig/rigor/cache.rbs +2 -3
- data/sig/rigor/effects/config_envelopes.rbs +100 -0
- data/sig/rigor/effects/effect_table.rbs +60 -0
- data/sig/rigor/effects/envelope.rbs +97 -0
- data/sig/rigor/effects/envelope_index.rbs +33 -0
- data/sig/rigor/effects/file_collection.rbs +81 -0
- data/sig/rigor/effects/label.rbs +26 -0
- data/sig/rigor/effects/label_set.rbs +46 -0
- data/sig/rigor/effects/method_key.rbs +24 -0
- data/sig/rigor/effects/origin.rbs +50 -0
- data/sig/rigor/effects/plugin_facts.rbs +141 -0
- data/sig/rigor/effects/registry.rbs +68 -0
- data/sig/rigor/effects/summary.rbs +50 -0
- data/sig/rigor/effects/taint_cause.rbs +12 -0
- data/sig/rigor/environment.rbs +17 -10
- data/sig/rigor/inference/optimistic_origin.rbs +10 -0
- data/sig/rigor/inference.rbs +9 -14
- data/sig/rigor/plugin/additional_initializer.rbs +36 -0
- data/sig/rigor/plugin/base.rbs +30 -7
- data/sig/rigor/plugin/effect_ancestry.rbs +27 -0
- data/sig/rigor/plugin/effect_attribution.rbs +66 -0
- data/sig/rigor/plugin/effect_edge.rbs +33 -0
- data/sig/rigor/plugin/effect_entry_points.rbs +25 -0
- data/sig/rigor/plugin/io_boundary.rbs +4 -0
- data/sig/rigor/plugin/loader.rbs +3 -3
- data/sig/rigor/plugin/manifest.rbs +51 -11
- data/sig/rigor/plugin/protocol_contract.rbs +68 -0
- data/sig/rigor/plugin/registry.rbs +62 -1
- data/sig/rigor/plugin.rbs +1 -1
- data/sig/rigor/rbs_extended.rbs +1 -1
- data/sig/rigor/reflection.rbs +9 -6
- data/sig/rigor/scope.rbs +148 -12
- data/sig/rigor/sig_gen/skip_reason_catalog.rbs +21 -0
- data/sig/rigor/source.rbs +4 -4
- data/sig/rigor/testing.rbs +10 -4
- data/sig/rigor/type.rbs +33 -1
- data/sig/rigor.rbs +39 -20
- data/skills/rigor-ask/SKILL.md +16 -11
- data/skills/rigor-baseline-reduce/SKILL.md +4 -6
- data/skills/rigor-ci-setup/SKILL.md +17 -21
- data/skills/rigor-doctor/SKILL.md +24 -22
- data/skills/rigor-doctor/references/01-checks.md +97 -33
- data/skills/rigor-editor-setup/SKILL.md +6 -4
- data/skills/rigor-mcp-setup/SKILL.md +5 -4
- data/skills/rigor-monkeypatch-resolve/SKILL.md +5 -3
- data/skills/rigor-next-steps/SKILL.md +4 -2
- data/skills/rigor-plugin-author/SKILL.md +19 -23
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +9 -8
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +25 -27
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +6 -8
- data/skills/rigor-plugin-review/SKILL.md +6 -4
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +2 -1
- data/skills/rigor-plugin-tune/SKILL.md +4 -2
- data/skills/rigor-project-init/SKILL.md +15 -10
- data/skills/rigor-project-init/references/01-detect.md +13 -9
- data/skills/rigor-project-init/references/02-configure.md +43 -14
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +14 -14
- data/skills/rigor-project-init/references/04-sig-uplift.md +27 -14
- data/skills/rigor-project-init/references/06-agent-contract.md +67 -0
- data/skills/rigor-protection-uplift/SKILL.md +4 -6
- data/skills/rigor-rbs-setup/SKILL.md +4 -2
- data/skills/rigor-type-oracle/SKILL.md +226 -0
- data/skills/rigor-type-oracle/references/01-oracle-commands.md +265 -0
- data/skills/rigor-type-oracle/references/02-agents-md-fragment.md +52 -0
- data/skills/rigor-type-oracle/references/03-gap-protocol.md +125 -0
- data/skills/rigor-unused-adjudicate/SKILL.md +12 -7
- data/skills/rigor-upgrade/SKILL.md +13 -8
- metadata +133 -3
- data/lib/rigor/cli/probe_environment.rb +0 -85
- data/sig/rigor/inference/builtins/numeric_catalog.rbs +0 -3
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "optionparser"
|
|
4
4
|
|
|
5
5
|
require_relative "../configuration"
|
|
6
|
+
require_relative "../effects/config_envelopes"
|
|
7
|
+
require_relative "../effects/registry"
|
|
6
8
|
require_relative "options"
|
|
7
9
|
require_relative "../sig_gen"
|
|
8
10
|
require_relative "command"
|
|
@@ -22,24 +24,30 @@ module Rigor
|
|
|
22
24
|
# AND the candidate is a `tighter-return`.
|
|
23
25
|
#
|
|
24
26
|
# Parameter policy defaults to `untyped`. `--params=observed` (slice 3) opts in to caller-side observation
|
|
25
|
-
# harvesting: the `ObservationCollector` walks `--observe=PATH...` (default
|
|
26
|
-
#
|
|
27
|
+
# harvesting: the `ObservationCollector` walks `--observe=PATH...` (default: the project's test roots,
|
|
28
|
+
# {Configuration#resolved_test_paths}), unions per-position arg types, and the generator emits the union per ADR-5
|
|
29
|
+
# clause 2.
|
|
27
30
|
# `--params=observed-strict` stays reserved-but-inert until the capability-role catalog ships (rejected with a usage
|
|
28
31
|
# error so the surface stays stable).
|
|
29
|
-
|
|
32
|
+
#
|
|
33
|
+
# `--check` (ADR-112 WD4) is the CI freshness gate: it runs the `--write` merge with every other flag as given,
|
|
34
|
+
# writes nothing, prints what would change, and exits 1 when anything would.
|
|
35
|
+
class SigGenCommand < Command # rubocop:disable Metrics/ClassLength
|
|
30
36
|
USAGE = "Usage: rigor sig-gen [options] [paths]"
|
|
31
37
|
|
|
32
|
-
VALID_MODES = %w[print diff write].freeze
|
|
38
|
+
VALID_MODES = %w[print diff write check].freeze
|
|
33
39
|
VALID_PARAM_POLICIES = %w[untyped observed observed-strict].freeze
|
|
34
40
|
VALID_FORMATS = %w[text json].freeze
|
|
35
41
|
|
|
36
|
-
# The skip reasons {#report_skipped} counts. The
|
|
37
|
-
# ({#report_unrenderable}, {#report_unresolvable_superclasses}
|
|
42
|
+
# The skip reasons {#report_skipped} counts. The four left out each have a report of their own
|
|
43
|
+
# ({#report_unrenderable}, {#report_unresolvable_superclasses}, {#report_inline_declared}, and the refusal
|
|
44
|
+
# lines `--write` / `--check` print for `inline_differs`), so a method never shows up in two tallies.
|
|
38
45
|
SUMMARISED_SKIP_REASONS = (SigGen::Classification::SKIP_DIAGNOSTIC_IDS.keys -
|
|
39
|
-
%i[unrenderable_rbs unresolvable_superclass
|
|
46
|
+
%i[unrenderable_rbs unresolvable_superclass inline_declared
|
|
47
|
+
inline_differs]).freeze
|
|
40
48
|
private_constant :SUMMARISED_SKIP_REASONS
|
|
41
49
|
|
|
42
|
-
# @return
|
|
50
|
+
# @return CLI exit status.
|
|
43
51
|
def run
|
|
44
52
|
options = parse_options
|
|
45
53
|
return CLI::EXIT_USAGE if options.nil?
|
|
@@ -50,17 +58,22 @@ module Rigor
|
|
|
50
58
|
observations = collect_observations(configuration, options)
|
|
51
59
|
generator = SigGen::Generator.new(configuration: configuration, paths: paths,
|
|
52
60
|
observations: observations,
|
|
53
|
-
include_private: options.fetch(:include_private)
|
|
61
|
+
include_private: options.fetch(:include_private),
|
|
62
|
+
effect_annotator: effect_annotator(configuration, paths, options),
|
|
63
|
+
overwrite: options.fetch(:overwrite))
|
|
54
64
|
candidates = generator.run
|
|
55
65
|
mode = options.fetch(:mode).to_sym
|
|
56
66
|
|
|
57
|
-
status =
|
|
58
|
-
|
|
67
|
+
status = case mode
|
|
68
|
+
when :write then dispatch_write(candidates, configuration, options)
|
|
69
|
+
when :check then dispatch_check(candidates, configuration, options)
|
|
59
70
|
else
|
|
60
71
|
dispatch_print_or_diff(candidates, mode, options)
|
|
61
72
|
0
|
|
62
73
|
end
|
|
63
74
|
report_skipped(candidates, options)
|
|
75
|
+
report_inline_declared(candidates, options)
|
|
76
|
+
report_withheld_annotations(candidates, options)
|
|
64
77
|
report_unrenderable(generator.unrenderable)
|
|
65
78
|
report_unresolvable_superclasses(generator.unresolvable_superclasses)
|
|
66
79
|
status
|
|
@@ -68,6 +81,74 @@ module Rigor
|
|
|
68
81
|
|
|
69
82
|
private
|
|
70
83
|
|
|
84
|
+
# ADR-103 WD9 — the effect table sig-gen writes annotations from, or `nil`.
|
|
85
|
+
#
|
|
86
|
+
# The gate is the project's own `effects:` opt-in and nothing else. An annotation is read back as an
|
|
87
|
+
# enforced envelope, so turning emission on from a sig-gen flag alone would let one command commit a
|
|
88
|
+
# project to a contract `rigor check` was never asked to keep; and a project that has not opted in
|
|
89
|
+
# pays neither the analysis this needs nor a single changed byte of output.
|
|
90
|
+
#
|
|
91
|
+
# `--effect-envelopes` is the second, narrower switch: `%a{pure}` is the ecosystem's existing purity
|
|
92
|
+
# spelling and round-trips through Steep as well as Rigor, while `%a{rigor:v1:effect …}` is Rigor's
|
|
93
|
+
# own and belongs in a project's `sig/` only when the author asked for it by name.
|
|
94
|
+
def effect_annotator(configuration, paths, options)
|
|
95
|
+
unless configuration.effects_enabled?
|
|
96
|
+
if options.fetch(:effect_envelopes)
|
|
97
|
+
@err.puts("rigor sig-gen: --effect-envelopes needs the `effects:` opt-in in .rigor.yml; " \
|
|
98
|
+
"no effect annotation was emitted.")
|
|
99
|
+
end
|
|
100
|
+
return nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
require_relative "check_runner_factory"
|
|
104
|
+
# Through the same factory `rigor check` and `rigor doctor` use, so the LRU cap, the worker
|
|
105
|
+
# resolution and the tolerated-effects switch cannot drift from the command whose cache this run
|
|
106
|
+
# shares. `workers: 0` because a collecting run is pinned to the sequential path anyway, and
|
|
107
|
+
# `--no-cache` mirrors `rigor check`'s flag of the same name.
|
|
108
|
+
runner = CheckRunnerFactory.build(
|
|
109
|
+
configuration: configuration,
|
|
110
|
+
options: { no_cache: options.fetch(:no_cache), explain: false, stats: false, workers: 0 },
|
|
111
|
+
buffer: nil, cache_root: configuration.cache_path
|
|
112
|
+
)
|
|
113
|
+
runner.run((configuration.paths + paths).uniq)
|
|
114
|
+
SigGen::EffectAnnotation::Annotator.new(
|
|
115
|
+
table: runner.effect_table, envelopes: options.fetch(:effect_envelopes),
|
|
116
|
+
envelope_index: runner.effect_envelopes, config_envelopes: config_envelopes(configuration)
|
|
117
|
+
)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# The project's `effects.envelopes:` entries, for gate 0's `match:` half. Built with a plain
|
|
121
|
+
# registry rather than the run's: gate 0 asks only WHETHER an entry selects this class, never what
|
|
122
|
+
# it bounds, so the vocabulary a plugin would add cannot change the answer.
|
|
123
|
+
def config_envelopes(configuration)
|
|
124
|
+
Effects::ConfigEnvelopes.build(
|
|
125
|
+
entries: configuration.effects_envelopes,
|
|
126
|
+
registry: Effects::Registry.for_configuration(configuration)
|
|
127
|
+
)
|
|
128
|
+
rescue StandardError
|
|
129
|
+
[]
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The withheld half of the emission, counted the way {#report_skipped} counts a skip: a method that
|
|
133
|
+
# did NOT get `%a{pure}` is the interesting case for a reader who expected one, and silence would
|
|
134
|
+
# read as "sig-gen does not do this" rather than "this method did not earn it".
|
|
135
|
+
def report_withheld_annotations(candidates, options)
|
|
136
|
+
return unless options.fetch(:format) == "text"
|
|
137
|
+
|
|
138
|
+
counts = candidates.each_with_object(Hash.new(0)) do |candidate, acc|
|
|
139
|
+
reason = candidate.effect_reason
|
|
140
|
+
acc[reason] += 1 if reason && reason != :emitted
|
|
141
|
+
end
|
|
142
|
+
return if counts.empty?
|
|
143
|
+
|
|
144
|
+
breakdown = counts.map { |reason, n| "#{SigGen::EffectAnnotation::DIAGNOSTIC_IDS.fetch(reason)}: #{n}" }
|
|
145
|
+
@err.puts(
|
|
146
|
+
"rigor sig-gen: withheld an effect annotation from #{counts.values.sum} method(s) " \
|
|
147
|
+
"(#{breakdown.join(', ')}). An annotation is enforced once written, so it is emitted only " \
|
|
148
|
+
"from an exhaustive, undischarged summary."
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
|
|
71
152
|
# Issue #778 — one stderr line per run saying how many methods the generator declined and why, so a
|
|
72
153
|
# method missing from the output is never a silent absence. Text mode only: under `--format=json` every
|
|
73
154
|
# skipped row is already in the payload with its `skip_reason`, and stderr stays clean for the consumer.
|
|
@@ -90,6 +171,18 @@ module Rigor
|
|
|
90
171
|
)
|
|
91
172
|
end
|
|
92
173
|
|
|
174
|
+
# ADR-112 WD4 — `sig_gen.inline_declared: skip` leaving methods out is the project's own choice, not a
|
|
175
|
+
# method sig-gen "could not type or would not overwrite", so it is counted on a line of its own.
|
|
176
|
+
def report_inline_declared(candidates, options)
|
|
177
|
+
return unless options.fetch(:format) == "text"
|
|
178
|
+
|
|
179
|
+
count = candidates.count { |c| c.skip_reason == :inline_declared }
|
|
180
|
+
return if count.zero?
|
|
181
|
+
|
|
182
|
+
@err.puts("rigor sig-gen: left #{count} method(s) declared inline out of sig/, as " \
|
|
183
|
+
"`sig_gen.inline_declared: skip` asks (sig.skipped.inline-declared).")
|
|
184
|
+
end
|
|
185
|
+
|
|
93
186
|
# A method whose rendered RBS does not parse is a Rigor rendering defect, not a fact about the user's
|
|
94
187
|
# code — the generator skipped it (so the rest of the signatures are still usable and still valid), but
|
|
95
188
|
# staying silent would leave the user with a quietly incomplete `sig/` and us with an unreported bug.
|
|
@@ -135,32 +228,101 @@ module Rigor
|
|
|
135
228
|
)
|
|
136
229
|
end
|
|
137
230
|
|
|
138
|
-
# @return
|
|
231
|
+
# @return exit status — non-zero when a file the user asked to write could not be written.
|
|
139
232
|
def dispatch_write(candidates, configuration, options)
|
|
140
|
-
|
|
141
|
-
path_mapper = SigGen::PathMapper.new(configuration: configuration, layout_index: layout_index)
|
|
142
|
-
writer = SigGen::Writer.new(path_mapper: path_mapper, overwrite: options.fetch(:overwrite))
|
|
143
|
-
|
|
144
|
-
results = writer.write_all(candidates)
|
|
233
|
+
results = build_writer(configuration, options).write_all(candidates)
|
|
145
234
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
#
|
|
235
|
+
refused = refused_candidates(candidates)
|
|
236
|
+
SigGen::Renderer.new(out: @out).render_write(results: results, format: options.fetch(:format), refused: refused)
|
|
237
|
+
SigGen::Renderer.refusal_lines(refused).each { |line| @err.puts(line) }
|
|
238
|
+
# A refused write (an assembled file that does not parse, an existing target that is not valid UTF-8,
|
|
239
|
+
# or a method whose inline and `sig/` declarations disagree without `--overwrite`) means the user asked for a
|
|
240
|
+
# write and did not get one, so the command must not report success — a green `sig-gen --write` in CI
|
|
241
|
+
# would otherwise mean nothing.
|
|
150
242
|
refusals = %i[skipped_invalid_rbs skipped_invalid_encoding]
|
|
151
|
-
results.any? { |result| refusals.include?(result.action) } ? 1 : 0
|
|
243
|
+
results.any? { |result| refusals.include?(result.action) } || !refused.empty? ? 1 : 0
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# ADR-112 WD4 — the methods whose inline and `sig/` declarations disagree, refused because the run did not
|
|
247
|
+
# pass `--overwrite`. Unlike every other skip, `sig/` contradicts the source while one stands.
|
|
248
|
+
def refused_candidates(candidates)
|
|
249
|
+
candidates.select { |candidate| candidate.skip_reason == :inline_differs }
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# ADR-112 WD4 — the same writer as {#dispatch_write}, flags included, in dry-run mode. Defined by what
|
|
253
|
+
# `--write` would do rather than by whether `--diff` prints anything: a tighter return against an existing
|
|
254
|
+
# declaration is a proposal `--write` declines without `--overwrite`, and a gate that counted it could
|
|
255
|
+
# never pass on a project that reviewed it and said no. With `--overwrite` it counts, because `--write
|
|
256
|
+
# --overwrite` would apply it.
|
|
257
|
+
#
|
|
258
|
+
# @return 1 when `sig/` is out of date (or a write would be refused), 0 when it is current.
|
|
259
|
+
def dispatch_check(candidates, configuration, options)
|
|
260
|
+
results = build_writer(configuration, options, dry_run: true).write_all(candidates)
|
|
261
|
+
refused = refused_candidates(candidates)
|
|
262
|
+
SigGen::Renderer.new(out: @out).render_check(results: results, format: options.fetch(:format),
|
|
263
|
+
refused: refused)
|
|
264
|
+
stale = SigGen::Renderer.out_of_date(results)
|
|
265
|
+
return 0 if stale.empty? && refused.empty?
|
|
266
|
+
|
|
267
|
+
report_check_failure(stale, refused) if options.fetch(:format) == "text"
|
|
268
|
+
1
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def report_check_failure(stale, refused)
|
|
272
|
+
unless stale.empty?
|
|
273
|
+
@err.puts("rigor sig-gen --check: #{stale.size} signature file(s) out of date; " \
|
|
274
|
+
"run `rigor sig-gen --write` with the same options to update them.")
|
|
275
|
+
end
|
|
276
|
+
return if refused.empty?
|
|
277
|
+
|
|
278
|
+
@err.puts("rigor sig-gen --check: #{refused.size} method(s) whose inline declaration and sig/ copy " \
|
|
279
|
+
"disagree; make them agree, or pass --overwrite to replace the sig/ member.")
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def build_writer(configuration, options, dry_run: false)
|
|
283
|
+
layout_index = SigGen::LayoutIndex.new(signature_paths: configuration.signature_paths)
|
|
284
|
+
path_mapper = SigGen::PathMapper.new(configuration: configuration, layout_index: layout_index)
|
|
285
|
+
SigGen::Writer.new(path_mapper: path_mapper, overwrite: options.fetch(:overwrite), dry_run: dry_run)
|
|
152
286
|
end
|
|
153
287
|
|
|
154
288
|
# Slice 3 — collect call-site argument observations when `--params=observed` is set. When `--observe=PATH` is not
|
|
155
|
-
# specified,
|
|
289
|
+
# specified, observe the project's test roots: `test_paths:`, or whichever of `spec/` and `test/` exist. With no
|
|
290
|
+
# root at all the run still succeeds, but says so — otherwise every parameter stays `untyped` with nothing
|
|
291
|
+
# naming the reason.
|
|
156
292
|
def collect_observations(configuration, options)
|
|
157
293
|
return {} if options.fetch(:params) != "observed"
|
|
158
294
|
|
|
159
295
|
observe_paths = options.fetch(:observe)
|
|
160
|
-
observe_paths =
|
|
296
|
+
observe_paths = configuration.resolved_test_paths if observe_paths.empty?
|
|
297
|
+
if observe_paths.empty?
|
|
298
|
+
warn_no_test_roots(configuration)
|
|
299
|
+
else
|
|
300
|
+
warn_missing_test_roots(observe_paths)
|
|
301
|
+
end
|
|
161
302
|
SigGen::ObservationCollector.new(configuration: configuration, paths: observe_paths).collect
|
|
162
303
|
end
|
|
163
304
|
|
|
305
|
+
# A declared root that does not exist is read as empty, so name it: `sig-gen` is the only command that reads
|
|
306
|
+
# the test roots, and without this the run looks exactly like one whose tests pass no typed arguments.
|
|
307
|
+
def warn_missing_test_roots(observe_paths)
|
|
308
|
+
missing = observe_paths.reject { |path| File.exist?(path) }
|
|
309
|
+
return if missing.empty?
|
|
310
|
+
|
|
311
|
+
consequence = missing.size == observe_paths.size ? "; every parameter stays untyped" : ""
|
|
312
|
+
@err.puts("rigor sig-gen: no call sites are observed from #{missing.map(&:inspect).join(', ')}, " \
|
|
313
|
+
"which does not exist#{consequence}. Check `test_paths:` or --observe=PATH.")
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def warn_no_test_roots(configuration)
|
|
317
|
+
reason = if configuration.test_paths.nil?
|
|
318
|
+
"no spec/ or test/ directory was found and `test_paths:` is unset"
|
|
319
|
+
else
|
|
320
|
+
"`test_paths:` is empty"
|
|
321
|
+
end
|
|
322
|
+
@err.puts("rigor sig-gen: --params=observed has no test roots to observe (#{reason}); every parameter " \
|
|
323
|
+
"stays untyped. Declare `test_paths:` in the configuration or pass --observe=PATH.")
|
|
324
|
+
end
|
|
325
|
+
|
|
164
326
|
def parse_options
|
|
165
327
|
options = {
|
|
166
328
|
mode: "print",
|
|
@@ -170,6 +332,8 @@ module Rigor
|
|
|
170
332
|
overwrite: false,
|
|
171
333
|
observe: [],
|
|
172
334
|
include_private: false,
|
|
335
|
+
effect_envelopes: false,
|
|
336
|
+
no_cache: false,
|
|
173
337
|
config: nil
|
|
174
338
|
}
|
|
175
339
|
build_option_parser(options).parse!(@argv)
|
|
@@ -181,23 +345,33 @@ module Rigor
|
|
|
181
345
|
nil
|
|
182
346
|
end
|
|
183
347
|
|
|
184
|
-
def build_option_parser(options) # rubocop:disable Metrics/AbcSize
|
|
348
|
+
def build_option_parser(options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
185
349
|
OptionParser.new do |opts| # rubocop:disable Metrics/BlockLength
|
|
186
350
|
opts.banner = USAGE
|
|
187
|
-
opts.on("--print", "Write RBS skeletons to stdout (default)") { options
|
|
188
|
-
opts.on("--diff", "Write a unified diff against existing RBS") { options
|
|
189
|
-
opts.on("--write", "Write generated RBS to sig/<path>.rbs files") { options
|
|
190
|
-
opts.on("--
|
|
351
|
+
opts.on("--print", "Write RBS skeletons to stdout (default)") { select_mode(options, "print") }
|
|
352
|
+
opts.on("--diff", "Write a unified diff against existing RBS") { select_mode(options, "diff") }
|
|
353
|
+
opts.on("--write", "Write generated RBS to sig/<path>.rbs files") { select_mode(options, "write") }
|
|
354
|
+
opts.on("--check", "Exit 1 when --write would change sig/; write nothing") { select_mode(options, "check") }
|
|
355
|
+
opts.on("--overwrite", "Allow tighter-return updates, and inline declarations that disagree with sig/, " \
|
|
356
|
+
"to replace user-authored RBS") do
|
|
191
357
|
options[:overwrite] = true
|
|
192
358
|
end
|
|
193
359
|
opts.on("--include-private", "Emit private / protected instance methods (default: public only)") do
|
|
194
360
|
options[:include_private] = true
|
|
195
361
|
end
|
|
362
|
+
opts.on("--effect-envelopes", "Also emit %a{rigor:v1:effect ...} for effectful methods " \
|
|
363
|
+
"(requires the effects: opt-in)") do
|
|
364
|
+
options[:effect_envelopes] = true
|
|
365
|
+
end
|
|
366
|
+
opts.on("--no-cache", "Do not read or write the analysis cache (effect collection only)") do
|
|
367
|
+
options[:no_cache] = true
|
|
368
|
+
end
|
|
196
369
|
opts.on("--format=FORMAT", "Output format: text or json") { |value| options[:format] = value }
|
|
197
370
|
opts.on("--params=POLICY", "Parameter policy: untyped (default), observed, observed-strict") do |value|
|
|
198
371
|
options[:params] = value
|
|
199
372
|
end
|
|
200
|
-
opts.on("--observe=PATH", "Directory / file to scan for call-site observations (repeatable
|
|
373
|
+
opts.on("--observe=PATH", "Directory / file to scan for call-site observations (repeatable; " \
|
|
374
|
+
"default: the configured test_paths)") do |value|
|
|
201
375
|
options[:observe] << value
|
|
202
376
|
end
|
|
203
377
|
opts.on("--new-files", "Emit only new-file classifications") do
|
|
@@ -213,12 +387,19 @@ module Rigor
|
|
|
213
387
|
end
|
|
214
388
|
end
|
|
215
389
|
|
|
390
|
+
# A second, different mode flag is a usage error rather than last-one-wins: `--check --write` must not
|
|
391
|
+
# quietly write in a CI job that meant to gate, nor `--write --check` quietly not.
|
|
392
|
+
def select_mode(options, mode)
|
|
393
|
+
options[:mode] = options[:mode_given] && options[:mode] != mode ? "conflict" : mode
|
|
394
|
+
options[:mode_given] = true
|
|
395
|
+
end
|
|
396
|
+
|
|
216
397
|
def validation_error(options)
|
|
217
398
|
mode = options.fetch(:mode)
|
|
218
399
|
format = options.fetch(:format)
|
|
219
400
|
params = options.fetch(:params)
|
|
220
401
|
|
|
221
|
-
return "--print, --diff, and --
|
|
402
|
+
return "--print, --diff, --write, and --check are mutually exclusive flags; pick one" if mode == "conflict"
|
|
222
403
|
return "unsupported --format=#{format}" unless VALID_FORMATS.include?(format)
|
|
223
404
|
return "unsupported --params=#{params}" unless VALID_PARAM_POLICIES.include?(params)
|
|
224
405
|
if params == "observed-strict"
|
|
@@ -74,9 +74,9 @@ module Rigor
|
|
|
74
74
|
|
|
75
75
|
# The bundled skills live at `<gem_root>/skills/`. From `lib/rigor/cli/skill_command.rb` that is three directories
|
|
76
76
|
# up.
|
|
77
|
-
SKILLS_ROOT = File.expand_path("../../../skills", __dir__)
|
|
77
|
+
SKILLS_ROOT = File.expand_path("../../../skills", __dir__).freeze
|
|
78
78
|
|
|
79
|
-
# @return
|
|
79
|
+
# @return CLI exit status.
|
|
80
80
|
def run
|
|
81
81
|
case @argv.first
|
|
82
82
|
when nil
|
|
@@ -37,22 +37,22 @@ module Rigor
|
|
|
37
37
|
# that could not run is NOT a clean check and the report must never let it read as one.
|
|
38
38
|
# - `route` / `reason` — the headline override, or nil to leave the presence-only recommendation standing.
|
|
39
39
|
Report = Data.define(:status, :detail, :route, :reason) do
|
|
40
|
-
# @return
|
|
40
|
+
# @return true when the check did not complete — the caller must then say so rather than let the
|
|
41
41
|
# fallback recommendation read as an all-clear.
|
|
42
42
|
def failed?
|
|
43
43
|
status == :error
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
# @param config
|
|
48
|
-
# @param root
|
|
47
|
+
# @param config — the config filename the presence probe found, relative to `root`.
|
|
48
|
+
# @param root — project root (the analysis itself resolves paths against the process cwd, as
|
|
49
49
|
# `rigor check` does).
|
|
50
50
|
def initialize(config:, root: Dir.pwd)
|
|
51
51
|
@config = config
|
|
52
52
|
@root = root
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
# @return
|
|
55
|
+
# @return never raises — every failure degrades to a `:error`/`:skipped` report whose `route` is nil,
|
|
56
56
|
# leaving the presence-only headline in charge.
|
|
57
57
|
def run
|
|
58
58
|
if @config.nil?
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "yaml"
|
|
4
4
|
|
|
5
|
+
require_relative "../plugin_gap_advisory"
|
|
6
|
+
|
|
5
7
|
module Rigor
|
|
6
8
|
class CLI
|
|
7
9
|
# The presence-only project-state probe behind `rigor skill describe` (ADR-73 WD2). It stats files and opens only
|
|
@@ -17,19 +19,11 @@ module Rigor
|
|
|
17
19
|
# RBS into the signature paths, so its presence is the signal that the gem-RBS gap has been addressed.
|
|
18
20
|
RBS_COLLECTION_LOCKFILE = "rbs_collection.lock.yaml"
|
|
19
21
|
|
|
20
|
-
# `Gemfile.lock` substrings that mark a Rails app, and the bundled Rails-family plugin ids — used to spot a
|
|
21
|
-
# configured Rails project that has not enabled any Rails plugin (a `rigor-plugin-tune` cue).
|
|
22
|
-
RAILS_LOCK_MARKERS = %w[railties actionpack activerecord actioncable].freeze
|
|
23
|
-
RAILS_PLUGIN_MARKERS = %w[
|
|
24
|
-
rigor-activerecord rigor-actionpack rigor-actionmailer rigor-activejob
|
|
25
|
-
rigor-rails-routes rigor-rails-i18n rigor-actioncable rigor-activestorage rigor-rails
|
|
26
|
-
].freeze
|
|
27
|
-
|
|
28
22
|
def initialize(root)
|
|
29
23
|
@root = root
|
|
30
24
|
end
|
|
31
25
|
|
|
32
|
-
# @return
|
|
26
|
+
# @return the presence-only state the recommendation routes on.
|
|
33
27
|
def to_h
|
|
34
28
|
config = CONFIG_FILENAMES.find { |name| File.file?(File.join(@root, name)) }
|
|
35
29
|
{
|
|
@@ -38,7 +32,7 @@ module Rigor
|
|
|
38
32
|
sig: File.directory?(File.join(@root, "sig")),
|
|
39
33
|
gems: File.file?(File.join(@root, "Gemfile.lock")),
|
|
40
34
|
rbs_collection: File.file?(File.join(@root, RBS_COLLECTION_LOCKFILE)),
|
|
41
|
-
|
|
35
|
+
plugins_unconfigured: plugins_unconfigured?(config),
|
|
42
36
|
ci: ci_state,
|
|
43
37
|
editor: editor_state,
|
|
44
38
|
mcp: mcp_state
|
|
@@ -47,24 +41,26 @@ module Rigor
|
|
|
47
41
|
|
|
48
42
|
private
|
|
49
43
|
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
|
|
44
|
+
# ADR-96 WD2 — true when the project depends on gems that bundled plugins model and enables none of those
|
|
45
|
+
# plugins, so `rigor-plugin-tune` buys more than community RBS would (the 20260620 field trial's strap
|
|
46
|
+
# case). Reads each plugin's `target_gems:` rather than the Rails-only table it replaces, so the cue
|
|
47
|
+
# covers every framework Rigor ships a plugin for. Only fires on an already-configured project; an
|
|
48
|
+
# un-configured app routes to `rigor-project-init`, which selects the plugins itself.
|
|
49
|
+
def plugins_unconfigured?(config)
|
|
55
50
|
return false if config.nil?
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
!file_mentions_any?(File.join(@root, config), RAILS_PLUGIN_MARKERS)
|
|
52
|
+
plugins = configured_plugins(File.join(@root, config))
|
|
53
|
+
PluginGapAdvisory.unconfigured?(project_root: @root, plugins: plugins)
|
|
61
54
|
end
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
56
|
+
# The probe reads the config file itself rather than {Rigor::Configuration}: it must stay a
|
|
57
|
+
# presence-only probe, and `Configuration.load` applies defaults (including the ADR-93 auto-wired
|
|
58
|
+
# `rigor-rbs-inline` entry) that have nothing to do with the question asked here.
|
|
59
|
+
def configured_plugins(path)
|
|
60
|
+
data = YAML.safe_load_file(path, permitted_classes: [], aliases: true)
|
|
61
|
+
data.is_a?(Hash) ? Array(data["plugins"]) : []
|
|
66
62
|
rescue StandardError
|
|
67
|
-
|
|
63
|
+
[]
|
|
68
64
|
end
|
|
69
65
|
|
|
70
66
|
# `:wired` (a CI config mentions `rigor`), `:unwired` (a CI config exists but does not), or `:none`.
|
|
@@ -121,9 +117,9 @@ module Rigor
|
|
|
121
117
|
# The entry-point SKILL itself — excluded from the catalogue because it is the skill being run, not a destination.
|
|
122
118
|
ENTRY_POINT_SKILL = "rigor-next-steps"
|
|
123
119
|
|
|
124
|
-
# Adoption-journey order for the catalogue and the order the recommendation decision tree walks.
|
|
125
|
-
#
|
|
126
|
-
#
|
|
120
|
+
# Adoption-journey order for the catalogue and the order the recommendation decision tree walks. The last two are
|
|
121
|
+
# journey-agnostic companions the agent can offer at any point, never presence-recommended steps:
|
|
122
|
+
# `rigor-type-oracle` fires on an event (being about to write a type), and `rigor-ask` on a question about Rigor.
|
|
127
123
|
CATALOG_ORDER = %w[
|
|
128
124
|
rigor-project-init
|
|
129
125
|
rigor-rbs-setup
|
|
@@ -137,12 +133,13 @@ module Rigor
|
|
|
137
133
|
rigor-plugin-author
|
|
138
134
|
rigor-upgrade
|
|
139
135
|
rigor-doctor
|
|
136
|
+
rigor-type-oracle
|
|
140
137
|
rigor-ask
|
|
141
138
|
].freeze
|
|
142
139
|
|
|
143
|
-
# @param skills
|
|
144
|
-
# @param root
|
|
145
|
-
# @param deep
|
|
140
|
+
# @param skills — discovered skills, each `{name:, path:}`.
|
|
141
|
+
# @param root — project root to probe (defaults to the cwd).
|
|
142
|
+
# @param deep — opt into `--deep`: run a real `rigor check` and let its result pick the headline. Off by
|
|
146
143
|
# default, and off is ADR-73 WD2's contract — see {SkillDeepProbe} for why the whole analysis path lives
|
|
147
144
|
# behind this one flag and in its own file.
|
|
148
145
|
def initialize(skills:, root: Dir.pwd, deep: false)
|
|
@@ -151,7 +148,6 @@ module Rigor
|
|
|
151
148
|
@deep = deep
|
|
152
149
|
end
|
|
153
150
|
|
|
154
|
-
# @return [String] the full describe report.
|
|
155
151
|
def render
|
|
156
152
|
catalog = catalog_skills
|
|
157
153
|
state = ProjectStateProbe.new(@root).to_h
|
|
@@ -202,14 +198,18 @@ module Rigor
|
|
|
202
198
|
def recommended_name_and_reason(state)
|
|
203
199
|
if state.fetch(:config).nil?
|
|
204
200
|
["rigor-project-init", "this project has no Rigor configuration yet — start here."]
|
|
205
|
-
elsif state.fetch(:
|
|
201
|
+
elsif state.fetch(:plugins_unconfigured)
|
|
206
202
|
["rigor-plugin-tune",
|
|
207
|
-
"
|
|
208
|
-
"
|
|
209
|
-
|
|
210
|
-
|
|
203
|
+
"your Gemfile declares gems Rigor ships plugins for and none is enabled — wire them so " \
|
|
204
|
+
"those framework calls resolve (a bigger win here than community RBS)."]
|
|
205
|
+
# ADR-73's second `rbs-setup` priority-softening case (2026-09-10): CI wiring is presence-only and
|
|
206
|
+
# side-effect-free, while community-RBS install is network-bound (`rbs collection install` hits
|
|
207
|
+
# rubygems.org). A configured project earns the cheap, local win first — checked ahead of the RBS
|
|
208
|
+
# gap rather than after it.
|
|
211
209
|
elsif state.fetch(:ci) != :wired
|
|
212
210
|
["rigor-ci-setup", "Rigor is configured but not wired into CI — lock in the regression guard."]
|
|
211
|
+
elsif state.fetch(:gems) && !state.fetch(:rbs_collection)
|
|
212
|
+
["rigor-rbs-setup", "your gems ship no community RBS yet — install it so Rigor stops typing them as Dynamic."]
|
|
213
213
|
# A present baseline is deliberately NOT a recommendation trigger. A baseline is a healthy, finished onboarding
|
|
214
214
|
# state, not a problem to work off; pushing every baselined project to "reduce it" turns a working build into a
|
|
215
215
|
# chore and tempts scattering `# rigor:disable` through the code to make a number go down — means over ends.
|
|
@@ -12,7 +12,7 @@ require_relative "../inference/flow_tracer"
|
|
|
12
12
|
require_relative "../inference/scope_indexer"
|
|
13
13
|
require_relative "command"
|
|
14
14
|
require_relative "trace_renderer"
|
|
15
|
-
require_relative "
|
|
15
|
+
require_relative "../project_environment"
|
|
16
16
|
|
|
17
17
|
module Rigor
|
|
18
18
|
class CLI
|
|
@@ -30,7 +30,7 @@ module Rigor
|
|
|
30
30
|
DEFAULT_KINDS = %i[bind union dispatch].freeze
|
|
31
31
|
VERBOSE_KINDS = (%i[enter result] + DEFAULT_KINDS).freeze
|
|
32
32
|
|
|
33
|
-
# @return
|
|
33
|
+
# @return CLI exit status.
|
|
34
34
|
def run
|
|
35
35
|
options = parse_options
|
|
36
36
|
file = @argv.first
|
|
@@ -92,10 +92,10 @@ module Rigor
|
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
# Mirrors the single-file path `rigor type-of` takes: the plugin-aware environment (so a trace replays the
|
|
95
|
-
# same dispatch `rigor check` would, including plugin-synthesized RBS — see {
|
|
95
|
+
# same dispatch `rigor check` would, including plugin-synthesized RBS — see {ProjectEnvironment}), an empty
|
|
96
96
|
# seed scope, one statement-level evaluation of the whole program — but recorded under the FlowTracer.
|
|
97
97
|
def record_events(root, file, configuration)
|
|
98
|
-
environment =
|
|
98
|
+
environment = ProjectEnvironment.build(configuration: configuration, source_files: [file])
|
|
99
99
|
scope = Scope.empty(environment: environment, source_path: file)
|
|
100
100
|
Inference::FlowTracer.record { scope.evaluate(root) }
|
|
101
101
|
end
|
|
@@ -28,9 +28,8 @@ module Rigor
|
|
|
28
28
|
BODY_HEIGHT_MIN = 3 # never shrink the source window below this
|
|
29
29
|
DEFAULT_SIZE = [24, 80].freeze
|
|
30
30
|
|
|
31
|
-
# @param
|
|
32
|
-
# @param
|
|
33
|
-
# @param file [String] display path.
|
|
31
|
+
# @param source — the traced file's source.
|
|
32
|
+
# @param file — display path.
|
|
34
33
|
def initialize(out:, source:, file:)
|
|
35
34
|
@out = out
|
|
36
35
|
@source = source
|
|
@@ -38,11 +37,11 @@ module Rigor
|
|
|
38
37
|
@lines = source.lines.map(&:chomp)
|
|
39
38
|
end
|
|
40
39
|
|
|
41
|
-
# @param events
|
|
40
|
+
# @param events — the
|
|
42
41
|
# pre-filtered frame list (the command owns kind filtering).
|
|
43
|
-
# @param delay
|
|
42
|
+
# @param delay — seconds between frames (autoplay);
|
|
44
43
|
# nil = step on key press when interactive.
|
|
45
|
-
# @param interactive
|
|
44
|
+
# @param interactive — whether to clear/redraw and wait.
|
|
46
45
|
def play(events, delay: nil, interactive: false)
|
|
47
46
|
@rows, @cols = interactive ? terminal_size : DEFAULT_SIZE
|
|
48
47
|
@interactive = interactive
|
|
@@ -22,7 +22,7 @@ module Rigor
|
|
|
22
22
|
USAGE = "Usage: rigor triage [options] [paths]"
|
|
23
23
|
DEFAULT_SECTIONS = %i[distribution selectors hotspots hints].freeze
|
|
24
24
|
|
|
25
|
-
# @return
|
|
25
|
+
# @return CLI exit status (always 0).
|
|
26
26
|
def run
|
|
27
27
|
options = parse_options
|
|
28
28
|
configuration = Configuration.load(options.fetch(:config))
|