rigortype 0.4.5 → 0.4.7
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/gem_overlay/activesupport/core_ext.rbs +16 -13
- data/data/vendored_gem_sigs/bundler/bundler.rbs +1 -1
- data/data/vendored_gem_sigs/rubygems/rubygems_extras.rbs +4 -3
- data/docs/handbook/01-getting-started.md +5 -2
- data/docs/handbook/11-sig-gen.md +61 -5
- data/docs/handbook/appendix-phpstan.md +4 -4
- data/docs/manual/02-cli-reference.md +192 -27
- data/docs/manual/03-configuration.md +38 -4
- data/docs/manual/04-diagnostics.md +1 -1
- data/docs/manual/06-baseline.md +6 -4
- data/docs/manual/09-editor-integration.md +13 -2
- data/docs/manual/10-mcp-server.md +47 -3
- data/docs/manual/12-caching.md +9 -1
- data/docs/manual/plugins/rigor-actionmailer.md +30 -0
- data/docs/manual/plugins/rigor-actionpack.md +49 -11
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +20 -1
- data/docs/manual/plugins/rigor-devise.md +15 -0
- data/docs/manual/plugins/rigor-rspec.md +2 -1
- data/lib/rigor/analysis/baseline.rb +24 -27
- data/lib/rigor/analysis/buffer_binding.rb +14 -0
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +5 -6
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/declaration_sourced_guard.rb +4 -4
- data/lib/rigor/analysis/check_rules/duplicate_hash_key_collector.rb +2 -2
- data/lib/rigor/analysis/check_rules/inferred_param_guard.rb +105 -12
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +143 -15
- data/lib/rigor/analysis/check_rules/lexical_method_sites.rb +4 -1
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +5 -8
- data/lib/rigor/analysis/check_rules/published_constant_guard.rb +1 -1
- data/lib/rigor/analysis/check_rules/rbs_receiver_surface.rb +66 -3
- data/lib/rigor/analysis/check_rules/rebinding_block_callees/dispatch.rb +112 -0
- data/lib/rigor/analysis/check_rules/rebinding_block_callees.rb +394 -0
- data/lib/rigor/analysis/check_rules/return_in_ensure_collector.rb +1 -2
- data/lib/rigor/analysis/check_rules/ruby_deprecations.rb +7 -0
- data/lib/rigor/analysis/check_rules/rule_ids.rb +1 -1
- data/lib/rigor/analysis/check_rules/rule_walk.rb +9 -7
- data/lib/rigor/analysis/check_rules/shadowed_rescue_collector.rb +3 -4
- data/lib/rigor/analysis/check_rules/special_global_setters.rb +3 -3
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +53 -2
- data/lib/rigor/analysis/check_rules/void_value_use_collector.rb +1 -1
- data/lib/rigor/analysis/check_rules.rb +217 -52
- data/lib/rigor/analysis/crash_signature.rb +2 -2
- data/lib/rigor/analysis/dependency_recorder.rb +15 -4
- data/lib/rigor/analysis/dependency_source_inference/boundary_cross_reporter.rb +14 -3
- data/lib/rigor/analysis/dependency_source_inference/gem_resolver.rb +1 -1
- data/lib/rigor/analysis/dependency_source_inference/index.rb +6 -8
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +4 -4
- data/lib/rigor/analysis/edit_replay.rb +154 -0
- data/lib/rigor/analysis/fact_store.rb +3 -3
- data/lib/rigor/analysis/incremental.rb +37 -0
- data/lib/rigor/analysis/incremental_run_slot/write_guard.rb +2 -2
- data/lib/rigor/analysis/incremental_run_slot.rb +2 -2
- data/lib/rigor/analysis/incremental_session.rb +202 -43
- data/lib/rigor/analysis/path_expansion.rb +41 -0
- data/lib/rigor/analysis/require_relative_closure.rb +100 -0
- data/lib/rigor/analysis/rule_catalog.rb +7 -3
- data/lib/rigor/analysis/run_disclosure_credit.rb +80 -0
- data/lib/rigor/analysis/run_disclosure_ledger.rb +155 -0
- data/lib/rigor/analysis/run_stats.rb +2 -2
- data/lib/rigor/analysis/runner/buffer_pool_dispatcher.rb +8 -4
- data/lib/rigor/analysis/runner/declaration_position.rb +1 -1
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +204 -96
- data/lib/rigor/analysis/runner/effect_envelope_pass.rb +1 -1
- data/lib/rigor/analysis/runner/pool_coordinator.rb +157 -320
- data/lib/rigor/analysis/runner/project_pre_passes.rb +48 -6
- data/lib/rigor/analysis/runner/run_level_facts.rb +304 -0
- data/lib/rigor/analysis/runner/run_snapshots.rb +44 -54
- data/lib/rigor/analysis/runner/scan_environment.rb +51 -0
- data/lib/rigor/analysis/runner/worker_payload.rb +36 -0
- data/lib/rigor/analysis/runner.rb +233 -120
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +1 -1
- data/lib/rigor/analysis/template_unit_positions.rb +2 -3
- data/lib/rigor/analysis/template_units.rb +9 -1
- data/lib/rigor/analysis/worker_session.rb +38 -33
- data/lib/rigor/bleeding_edge.rb +3 -6
- data/lib/rigor/builtins/imported_refinements.rb +6 -8
- data/lib/rigor/builtins/predefined_constant_refinements.rb +1 -1
- data/lib/rigor/builtins/regex_refinement.rb +4 -6
- data/lib/rigor/cache/descriptor.rb +6 -5
- data/lib/rigor/cache/incremental_snapshot.rb +60 -18
- data/lib/rigor/cache/rbs_cache_producer.rb +2 -2
- data/lib/rigor/cache/rbs_class_ancestor_table.rb +1 -1
- data/lib/rigor/cache/rbs_class_type_param_names.rb +2 -2
- data/lib/rigor/cache/rbs_constant_table.rb +1 -1
- data/lib/rigor/cache/rbs_descriptor.rb +54 -3
- data/lib/rigor/cache/rbs_environment.rb +4 -1
- data/lib/rigor/cache/rbs_environment_marshal_patch.rb +2 -2
- data/lib/rigor/cache/rbs_known_class_names.rb +1 -1
- data/lib/rigor/cache/store.rb +11 -10
- data/lib/rigor/ci_detector.rb +1 -1
- data/lib/rigor/cli/annotate_command.rb +24 -19
- data/lib/rigor/cli/baseline_command.rb +145 -55
- data/lib/rigor/cli/catalogue.rb +256 -0
- data/lib/rigor/cli/check_command.rb +188 -93
- data/lib/rigor/cli/check_runner_factory.rb +2 -2
- data/lib/rigor/cli/command.rb +15 -1
- data/lib/rigor/cli/coverage_command.rb +21 -8
- data/lib/rigor/cli/coverage_mutation.rb +5 -7
- data/lib/rigor/cli/coverage_renderer.rb +2 -1
- data/lib/rigor/cli/coverage_scan.rb +3 -5
- data/lib/rigor/cli/diff_command.rb +27 -21
- data/lib/rigor/cli/docs_command.rb +1 -2
- data/lib/rigor/cli/doctor_command.rb +12 -12
- data/lib/rigor/cli/effects_command.rb +50 -40
- data/lib/rigor/cli/effects_diff_renderer.rb +18 -17
- data/lib/rigor/cli/effects_explain_renderer.rb +13 -12
- data/lib/rigor/cli/effects_renderer.rb +2 -1
- data/lib/rigor/cli/effects_snapshot_command.rb +18 -17
- data/lib/rigor/cli/explain_command.rb +18 -8
- data/lib/rigor/cli/fused_protection_renderer.rb +2 -1
- data/lib/rigor/cli/json_document.rb +41 -0
- data/lib/rigor/cli/lens_command.rb +17 -13
- data/lib/rigor/cli/lsp_command.rb +250 -23
- data/lib/rigor/cli/mcp_command.rb +14 -8
- data/lib/rigor/cli/mutation_fork_scan.rb +1 -1
- data/lib/rigor/cli/mutation_protection_renderer.rb +3 -2
- data/lib/rigor/cli/options.rb +60 -4
- data/lib/rigor/cli/plugin_command.rb +93 -46
- data/lib/rigor/cli/plugins_command.rb +22 -18
- data/lib/rigor/cli/plugins_renderer.rb +34 -8
- data/lib/rigor/cli/protection_fork_scan.rb +2 -2
- data/lib/rigor/cli/protection_renderer.rb +2 -1
- data/lib/rigor/cli/show_bleedingedge_command.rb +23 -16
- data/lib/rigor/cli/sig_gen_command.rb +130 -34
- data/lib/rigor/cli/skill_command.rb +32 -3
- data/lib/rigor/cli/skill_deep_probe.rb +1 -1
- data/lib/rigor/cli/skill_describe.rb +1 -1
- data/lib/rigor/cli/trace_command.rb +14 -9
- data/lib/rigor/cli/triage_command.rb +10 -11
- data/lib/rigor/cli/triage_renderer.rb +2 -1
- data/lib/rigor/cli/type_of_command.rb +18 -12
- data/lib/rigor/cli/type_of_project_scope.rb +24 -0
- data/lib/rigor/cli/type_of_renderer.rb +4 -1
- data/lib/rigor/cli/type_scan_command.rb +14 -9
- data/lib/rigor/cli/type_scan_renderer.rb +2 -1
- data/lib/rigor/cli/unused_command.rb +27 -17
- data/lib/rigor/cli/upgrade_command.rb +5 -0
- data/lib/rigor/cli.rb +209 -54
- data/lib/rigor/config_audit.rb +17 -6
- data/lib/rigor/configuration/severity_profile.rb +5 -5
- data/lib/rigor/configuration.rb +86 -34
- data/lib/rigor/effects/definition_context.rb +0 -2
- data/lib/rigor/effects/envelope.rb +1 -1
- data/lib/rigor/effects/framework_units.rb +1 -1
- data/lib/rigor/effects/mutation_classifier.rb +4 -7
- data/lib/rigor/effects/propagator.rb +1 -1
- data/lib/rigor/effects/registry.rb +2 -2
- data/lib/rigor/effects/snapshot.rb +1 -1
- data/lib/rigor/effects/taint_cause.rb +0 -2
- data/lib/rigor/effects/unit_scan.rb +9 -13
- data/lib/rigor/environment/bundle_sig_discovery.rb +5 -9
- data/lib/rigor/environment/class_registry.rb +3 -3
- data/lib/rigor/environment/core_root.rb +77 -0
- data/lib/rigor/environment/default_libraries.rb +1 -1
- data/lib/rigor/environment/deferred_signature_plan.rb +283 -0
- data/lib/rigor/environment/gated_signature_guard.rb +1 -1
- data/lib/rigor/environment/gated_signature_plan.rb +5 -2
- data/lib/rigor/environment/lockfile_resolver.rb +28 -12
- data/lib/rigor/environment/member_consistency/comparator.rb +18 -1
- data/lib/rigor/environment/member_consistency.rb +56 -7
- data/lib/rigor/environment/missing_gem_constant_index.rb +3 -2
- data/lib/rigor/environment/project_path_gems.rb +124 -0
- data/lib/rigor/environment/rbs_hierarchy.rb +6 -15
- data/lib/rigor/environment/rbs_loader.rb +592 -70
- data/lib/rigor/environment/reflection.rb +7 -7
- data/lib/rigor/environment/required_features.rb +136 -32
- data/lib/rigor/environment/ruby_library_roots.rb +122 -0
- data/lib/rigor/environment/vendored_signature_plan.rb +153 -0
- data/lib/rigor/environment.rb +44 -11
- data/lib/rigor/flow_contribution/merger.rb +4 -4
- data/lib/rigor/inference/acceptance.rb +93 -32
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/block_run_count.rb +178 -0
- data/lib/rigor/inference/budget_trace.rb +8 -2
- data/lib/rigor/inference/builtins/exception_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/numeric_catalog.rb +2 -5
- data/lib/rigor/inference/builtins/random_catalog.rb +2 -2
- data/lib/rigor/inference/callee_mutation_chain.rb +126 -0
- data/lib/rigor/inference/callee_write_census.rb +815 -0
- data/lib/rigor/inference/construction_escape.rb +217 -0
- data/lib/rigor/inference/declaration_walk/traversal.rb +1 -1
- data/lib/rigor/inference/deep_erasure.rb +170 -0
- data/lib/rigor/inference/def_handle.rb +1 -1
- data/lib/rigor/inference/def_node_resolver.rb +1 -1
- data/lib/rigor/inference/def_return_typer.rb +204 -12
- data/lib/rigor/inference/definer_resolution.rb +21 -5
- data/lib/rigor/inference/definers_index.rb +106 -0
- data/lib/rigor/inference/direct_ivar_writes.rb +300 -0
- data/lib/rigor/inference/element_read_widening.rb +49 -3
- data/lib/rigor/inference/error_info.rb +17 -3
- data/lib/rigor/inference/expression_typer.rb +173 -57
- data/lib/rigor/inference/external_ancestor_resolution.rb +3 -4
- data/lib/rigor/inference/global_write_census.rb +110 -1
- data/lib/rigor/inference/global_writer_reach.rb +260 -0
- data/lib/rigor/inference/hkt_body.rb +1 -2
- data/lib/rigor/inference/hkt_body_parser.rb +3 -5
- data/lib/rigor/inference/hkt_reducer.rb +2 -2
- data/lib/rigor/inference/hkt_registry.rb +3 -5
- data/lib/rigor/inference/indexed_narrowing.rb +4 -7
- data/lib/rigor/inference/intervening_ivar_reset.rb +26 -4
- data/lib/rigor/inference/ivar_writer_census.rb +246 -0
- data/lib/rigor/inference/key_presence_guard.rb +2 -1
- data/lib/rigor/inference/loop_value_record.rb +57 -0
- data/lib/rigor/inference/match_rebinding/frame.rb +65 -0
- data/lib/rigor/inference/match_rebinding/yielding_match.rb +45 -0
- data/lib/rigor/inference/match_rebinding.rb +11 -1
- data/lib/rigor/inference/memo_audit.rb +55 -0
- data/lib/rigor/inference/method_dispatcher/alias_strict_nominals.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/argument_distribution.rb +40 -0
- data/lib/rigor/inference/method_dispatcher/block_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +113 -12
- data/lib/rigor/inference/method_dispatcher/contested_overloads.rb +89 -0
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +7 -3
- data/lib/rigor/inference/method_dispatcher/no_match_fallback.rb +54 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +39 -27
- data/lib/rigor/inference/method_dispatcher/random_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +203 -17
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/self_substitute.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/set_folding.rb +59 -24
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +83 -20
- data/lib/rigor/inference/method_dispatcher/singleton_folding.rb +0 -1
- data/lib/rigor/inference/method_dispatcher/singleton_mixin_dispatch.rb +50 -17
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +5 -5
- data/lib/rigor/inference/method_dispatcher/struct_materialization.rb +4 -6
- data/lib/rigor/inference/method_dispatcher/uri_folding.rb +1 -1
- data/lib/rigor/inference/method_dispatcher.rb +109 -29
- data/lib/rigor/inference/module_function_state/spellings.rb +64 -0
- data/lib/rigor/inference/module_function_state.rb +23 -1
- data/lib/rigor/inference/multi_target_binder.rb +1 -1
- data/lib/rigor/inference/mutation_rejoin.rb +240 -4
- data/lib/rigor/inference/mutation_widening.rb +162 -29
- data/lib/rigor/inference/narrowing.rb +113 -38
- data/lib/rigor/inference/object_mixins.rb +13 -1
- data/lib/rigor/inference/optimistic_origin.rb +1 -1
- data/lib/rigor/inference/out_buffer_mutation.rb +72 -0
- data/lib/rigor/inference/parameter_inference_collector.rb +5 -6
- data/lib/rigor/inference/pre_eval_constants.rb +1 -1
- data/lib/rigor/inference/project_method_ownership.rb +6 -3
- data/lib/rigor/inference/project_seed_tables.rb +2 -1
- data/lib/rigor/inference/project_yielder.rb +122 -0
- data/lib/rigor/inference/receiver_alias.rb +20 -0
- data/lib/rigor/inference/refined_dispatch/constructed_class.rb +3 -1
- data/lib/rigor/inference/refined_dispatch/own_method.rb +2 -1
- data/lib/rigor/inference/remover_holder_scan.rb +233 -0
- data/lib/rigor/inference/remover_narrowing.rb +224 -0
- data/lib/rigor/inference/rewrite_mutation.rb +53 -2
- data/lib/rigor/inference/scope_indexer/certainty.rb +9 -1
- data/lib/rigor/inference/scope_indexer/constant_ivar_value.rb +93 -0
- data/lib/rigor/inference/scope_indexer/declared_ivar_types.rb +64 -0
- data/lib/rigor/inference/scope_indexer/declared_param_writes.rb +205 -0
- data/lib/rigor/inference/scope_indexer/derived_param_value.rb +112 -0
- data/lib/rigor/inference/scope_indexer/empty_seed.rb +99 -0
- data/lib/rigor/inference/scope_indexer/ivar_contributions.rb +135 -0
- data/lib/rigor/inference/scope_indexer/unseen_ivar_writers.rb +133 -0
- data/lib/rigor/inference/scope_indexer.rb +614 -146
- data/lib/rigor/inference/self_call_ivar_writes.rb +101 -17
- data/lib/rigor/inference/set_mutation.rb +189 -0
- data/lib/rigor/inference/singleton_hook_decline.rb +1 -2
- data/lib/rigor/inference/statement_evaluator.rb +3036 -278
- data/lib/rigor/inference/struct_fold_safety.rb +4 -6
- data/lib/rigor/inference/synthetic_method_index.rb +23 -0
- data/lib/rigor/inference/synthetic_method_scanner/project_members.rb +130 -0
- data/lib/rigor/inference/synthetic_method_scanner.rb +73 -14
- data/lib/rigor/inference/toplevel_def_census.rb +59 -0
- data/lib/rigor/inference/unknown_store_widening.rb +103 -4
- data/lib/rigor/inference/unthreaded_rebinds.rb +2 -3
- data/lib/rigor/inference/void_tail_summary.rb +2 -1
- data/lib/rigor/language_server/buffer_table.rb +9 -3
- data/lib/rigor/language_server/completion_provider.rb +26 -20
- data/lib/rigor/language_server/config_warning_announcer.rb +28 -0
- data/lib/rigor/language_server/diagnostic_publisher.rb +28 -9
- data/lib/rigor/language_server/document_symbol_provider.rb +1 -1
- data/lib/rigor/language_server/folding_range_provider.rb +1 -2
- data/lib/rigor/language_server/hover_provider.rb +1 -7
- data/lib/rigor/language_server/hover_renderer.rb +6 -8
- data/lib/rigor/language_server/project_context.rb +98 -4
- data/lib/rigor/language_server/publish_batcher.rb +3 -4
- data/lib/rigor/language_server/selection_range_provider.rb +2 -4
- data/lib/rigor/language_server/server.rb +12 -6
- data/lib/rigor/language_server/signature_help_provider.rb +4 -4
- data/lib/rigor/lens/plugins.rb +1 -1
- data/lib/rigor/mcp/server.rb +82 -3
- data/lib/rigor/mcp.rb +1 -1
- data/lib/rigor/plugin/access_denied_error.rb +1 -2
- data/lib/rigor/plugin/base.rb +26 -11
- data/lib/rigor/plugin/bundled_catalog.rb +1 -12
- data/lib/rigor/plugin/io_boundary.rb +1 -1
- data/lib/rigor/plugin/loader.rb +25 -8
- data/lib/rigor/plugin/macro/heredoc_template.rb +7 -8
- data/lib/rigor/plugin/macro/trait_registry.rb +6 -9
- data/lib/rigor/plugin/manifest.rb +38 -16
- data/lib/rigor/plugin/registry.rb +41 -3
- data/lib/rigor/plugin/services.rb +2 -3
- data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +11 -0
- data/lib/rigor/plugin/trust_policy.rb +2 -2
- data/lib/rigor/plugin/type_node_resolver.rb +2 -4
- data/lib/rigor/plugin.rb +6 -8
- data/lib/rigor/plugin_gap_advisory.rb +5 -1
- data/lib/rigor/project_environment.rb +16 -2
- data/lib/rigor/protection/analysis_guard.rb +1 -1
- data/lib/rigor/protection/discovery_seed.rb +96 -2
- data/lib/rigor/protection/mutator.rb +1 -1
- data/lib/rigor/rbs_extended/envelope_scanner.rb +4 -6
- data/lib/rigor/rbs_extended/hkt_directives.rb +6 -10
- data/lib/rigor/rbs_extended/reporter.rb +27 -3
- data/lib/rigor/rbs_extended.rb +2 -3
- data/lib/rigor/reflection/constant_ancestors.rb +5 -3
- data/lib/rigor/rooted_glob.rb +25 -2
- data/lib/rigor/run_scoped_slots.rb +38 -0
- data/lib/rigor/runtime/jit.rb +1 -1
- data/lib/rigor/scope/discovery_index.rb +19 -6
- data/lib/rigor/scope/resolution_chain.rb +3 -1
- data/lib/rigor/scope.rb +379 -36
- data/lib/rigor/sig_gen/classification.rb +4 -0
- data/lib/rigor/sig_gen/effect_annotation.rb +3 -3
- data/lib/rigor/sig_gen/generator.rb +668 -84
- data/lib/rigor/sig_gen/generic_header_scan.rb +189 -0
- data/lib/rigor/sig_gen/inline_declarations.rb +2 -2
- data/lib/rigor/sig_gen/layout_index.rb +3 -3
- data/lib/rigor/sig_gen/method_candidate.rb +23 -4
- data/lib/rigor/sig_gen/observation_collector.rb +7 -6
- data/lib/rigor/sig_gen/observed_call.rb +1 -1
- data/lib/rigor/sig_gen/path_mapper.rb +6 -0
- data/lib/rigor/sig_gen/rbs_validity.rb +1 -1
- data/lib/rigor/sig_gen/renderer.rb +21 -12
- data/lib/rigor/sig_gen/skip_reason_catalog.rb +15 -2
- data/lib/rigor/sig_gen/type_elaborator.rb +21 -5
- data/lib/rigor/sig_gen/type_name_qualification.rb +69 -0
- data/lib/rigor/sig_gen/write_reporter.rb +63 -14
- data/lib/rigor/sig_gen/write_result.rb +23 -12
- data/lib/rigor/sig_gen/writer.rb +217 -37
- data/lib/rigor/type/acceptance_router.rb +2 -2
- data/lib/rigor/type/accepts_result.rb +1 -1
- data/lib/rigor/type/app.rb +4 -6
- data/lib/rigor/type/combinator.rb +13 -16
- data/lib/rigor/type/constant.rb +48 -7
- data/lib/rigor/type/hash_shape.rb +26 -40
- data/lib/rigor/type/struct_instance.rb +3 -2
- data/lib/rigor/type/union.rb +1 -1
- data/lib/rigor/type_node/identifier.rb +1 -2
- data/lib/rigor/type_node/resolver_chain.rb +1 -1
- data/lib/rigor/type_node/string_literal.rb +1 -2
- data/lib/rigor/type_node/symbol_literal.rb +1 -2
- data/lib/rigor/type_node/union.rb +3 -6
- data/lib/rigor/value_semantics.rb +1 -10
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +2 -2
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +2 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +1 -1
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +89 -70
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +4 -2
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +25 -4
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +1 -5
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_scan.rb +0 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/erb_compiler.rb +2 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/view_assigns.rb +1 -1
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +79 -19
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/effects.rb +1 -1
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +6 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/association_roots.rb +3 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/effects.rb +2 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +21 -31
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +10 -16
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +2 -3
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +0 -1
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +5 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +3 -4
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +1 -1
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +7 -12
- data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/effects.rb +1 -1
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext/symbolize_keys.rb +102 -0
- data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +19 -6
- data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +26 -13
- data/plugins/rigor-alba/lib/rigor/plugin/alba/resource_index.rb +3 -3
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +5 -3
- data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +1 -3
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +6 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +5 -6
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +7 -6
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/params_shape.rb +2 -3
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +5 -8
- data/plugins/rigor-ethon/lib/rigor/plugin/ethon.rb +0 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +1 -1
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +10 -18
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/analyzer.rb +0 -1
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi/discoverer.rb +0 -2
- data/plugins/rigor-ffi/lib/rigor/plugin/ffi.rb +4 -5
- data/plugins/rigor-grape/lib/rigor/plugin/grape.rb +0 -2
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +6 -7
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +2 -4
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +2 -5
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +2 -2
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +6 -6
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +2 -1
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +3 -8
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +2 -3
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +5 -8
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +1 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +2 -3
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_discoverer.rb +1 -1
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +4 -11
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +1 -1
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +2 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +102 -11
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +2 -2
- data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +3 -4
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +6 -8
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +5 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +6 -7
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +2 -2
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +17 -21
- data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +2 -3
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_discoverer.rb +3 -2
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer/serializer_index.rb +4 -4
- data/plugins/rigor-typelizer/lib/rigor/plugin/typelizer.rb +1 -1
- data/sig/rigor/analysis/baseline.rbs +0 -2
- data/sig/rigor/analysis/fact_store.rbs +1 -0
- data/sig/rigor/analysis/reachability/scan_cache.rbs +1 -1
- data/sig/rigor/cli/diff_command.rbs +1 -1
- data/sig/rigor/cli/explain_command.rbs +1 -1
- data/sig/rigor/effects/file_collection.rbs +5 -6
- data/sig/rigor/effects/registry.rbs +8 -6
- data/sig/rigor/effects/summary.rbs +6 -5
- data/sig/rigor/environment.rbs +5 -3
- data/sig/rigor/inference/optimistic_origin.rbs +11 -0
- data/sig/rigor/inference.rbs +10 -1
- data/sig/rigor/plugin/additional_initializer.rbs +0 -5
- data/sig/rigor/plugin/base.rbs +1 -0
- data/sig/rigor/plugin/effect_ancestry.rbs +6 -4
- data/sig/rigor/plugin/effect_attribution.rbs +25 -10
- data/sig/rigor/plugin/effect_edge.rbs +12 -5
- data/sig/rigor/plugin/effect_entry_points.rbs +8 -3
- data/sig/rigor/plugin/io_boundary.rbs +6 -5
- data/sig/rigor/plugin/manifest.rbs +16 -18
- data/sig/rigor/plugin/protocol_contract.rbs +6 -9
- data/sig/rigor/plugin/registry.rbs +1 -1
- data/sig/rigor/scope.rbs +51 -50
- data/sig/rigor/type.rbs +0 -6
- data/skills/rigor-plugin-author/SKILL.md +4 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +5 -5
- data/skills/rigor-plugin-review/SKILL.md +3 -3
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +12 -1
- metadata +55 -3
data/lib/rigor/mcp/server.rb
CHANGED
|
@@ -155,6 +155,10 @@ module Rigor
|
|
|
155
155
|
].freeze
|
|
156
156
|
Ractor.make_shareable(TOOLS)
|
|
157
157
|
|
|
158
|
+
# The tools that read a configuration — the ones taking a `config` argument — and so report its warnings.
|
|
159
|
+
CONFIG_TOOLS = TOOLS.filter_map { |tool| tool[:name] if tool.dig(:inputSchema, :properties, :config) }.freeze
|
|
160
|
+
Ractor.make_shareable(CONFIG_TOOLS)
|
|
161
|
+
|
|
158
162
|
def initialize(config_path: nil, err: $stderr)
|
|
159
163
|
@config_path = config_path
|
|
160
164
|
@err = err
|
|
@@ -165,7 +169,7 @@ module Rigor
|
|
|
165
169
|
id = request["id"]
|
|
166
170
|
method_name = request["method"]
|
|
167
171
|
|
|
168
|
-
#
|
|
172
|
+
# A notification carries no `id` and requires no response.
|
|
169
173
|
return nil if id.nil?
|
|
170
174
|
|
|
171
175
|
case method_name
|
|
@@ -181,8 +185,38 @@ module Rigor
|
|
|
181
185
|
end
|
|
182
186
|
end
|
|
183
187
|
|
|
188
|
+
# ADR-126 WD4 — `rigor_type_of` and `rigor_explain` print documents whose top-level shape changes in the next
|
|
189
|
+
# minor, and their CLI says so on stderr, which `tool_result` drops once there is a document. The notice is
|
|
190
|
+
# carried to the agent as its own `text` content block, the same text the CLI printed, so the document block
|
|
191
|
+
# stays byte-for-byte what the CLI wrote. When stdout is empty the error text already is stderr.
|
|
192
|
+
SHAPE_NOTICE_TOOLS = %w[rigor_type_of rigor_explain].freeze
|
|
193
|
+
|
|
184
194
|
private
|
|
185
195
|
|
|
196
|
+
def finish_tool_result(name, args, out, err, exit_code)
|
|
197
|
+
result = with_config_warnings(tool_result(out, err, exit_code), name, args, out)
|
|
198
|
+
result = with_shape_notice(result, name, out, err)
|
|
199
|
+
with_path_warnings(result, name, out, err)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# #2206 — `rigor coverage` writes its path warnings (a missing path skipped beside files it measures) to stderr,
|
|
203
|
+
# which a successful run drops. The tool carries the CLI's whole stderr (path warnings and any other warnings)
|
|
204
|
+
# as a second text block, unfiltered, so an agent sees what was skipped.
|
|
205
|
+
def with_path_warnings(result, name, out, err)
|
|
206
|
+
return result unless name == "rigor_coverage" && !out.empty? && !err.strip.empty?
|
|
207
|
+
|
|
208
|
+
result.merge(content: result.fetch(:content) + [{ type: "text", text: err }])
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def with_shape_notice(result, name, out, err)
|
|
212
|
+
return result unless SHAPE_NOTICE_TOOLS.include?(name) && !out.empty?
|
|
213
|
+
|
|
214
|
+
notice = err[CLI::JsonDocument::NOTICE_PATTERN]
|
|
215
|
+
return result unless notice
|
|
216
|
+
|
|
217
|
+
result.merge(content: result.fetch(:content) + [{ type: "text", text: notice }])
|
|
218
|
+
end
|
|
219
|
+
|
|
186
220
|
def handle_initialize(id)
|
|
187
221
|
require_relative "../version"
|
|
188
222
|
success(id, {
|
|
@@ -201,7 +235,7 @@ module Rigor
|
|
|
201
235
|
require_relative "../cli"
|
|
202
236
|
exit_code = with_captured_streams(out_io, err_io) { CLI.new(argv, out: out_io, err: err_io).run }
|
|
203
237
|
|
|
204
|
-
success(id,
|
|
238
|
+
success(id, finish_tool_result(name, args, out_io.string, err_io.string, exit_code))
|
|
205
239
|
rescue SystemExit => e
|
|
206
240
|
# Defence against any command that exits: it must not take the server down with it.
|
|
207
241
|
success(id, { content: [{ type: "text", text: "Command exited (status #{e.status})" }], isError: true })
|
|
@@ -221,6 +255,43 @@ module Rigor
|
|
|
221
255
|
{ content: [{ type: "text", text: out.empty? ? err : out }], isError: is_error }
|
|
222
256
|
end
|
|
223
257
|
|
|
258
|
+
# Issue #2231 — `rigor check` puts {ConfigAudit} warnings in its JSON `config_warnings`; the other tools read the
|
|
259
|
+
# same configuration and said nothing about it, so an agent saw `call.undefined-method` on a project whose
|
|
260
|
+
# `.rigor.yml` misspelt the key meant to silence it and got no hint why. Every tool that takes a `config`
|
|
261
|
+
# argument now carries them: a second `text` content block holding `{"config_warnings": [...]}`, each entry the
|
|
262
|
+
# shape check's JSON uses. The tool's own document is left byte-for-byte as its CLI printed it, and an output
|
|
263
|
+
# that already carries the key (check's) gets no second copy. Nothing is added when there is nothing to say,
|
|
264
|
+
# nor when the configuration does not load — the CLI has already reported that.
|
|
265
|
+
def with_config_warnings(result, name, args, out)
|
|
266
|
+
return result unless CONFIG_TOOLS.include?(name)
|
|
267
|
+
|
|
268
|
+
warnings = config_warnings(present_config(args["config"]) || @config_path)
|
|
269
|
+
return result if warnings.empty? || carries_config_warnings?(out)
|
|
270
|
+
|
|
271
|
+
block = { type: "text", text: JSON.generate({ "config_warnings" => warnings.map(&:to_h) }) }
|
|
272
|
+
result.merge(content: result.fetch(:content) + [block])
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Only a configuration that does not load is "no warnings to show": the CLI has already reported that error.
|
|
276
|
+
# A failure in ConfigAudit itself is a bug and surfaces as an internal error rather than reading as clean.
|
|
277
|
+
def config_warnings(config_path)
|
|
278
|
+
require_relative "../configuration"
|
|
279
|
+
require_relative "../config_audit"
|
|
280
|
+
begin
|
|
281
|
+
configuration = Configuration.load(config_path)
|
|
282
|
+
rescue ConfigurationError
|
|
283
|
+
return []
|
|
284
|
+
end
|
|
285
|
+
ConfigAudit.warnings(configuration)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def carries_config_warnings?(out)
|
|
289
|
+
document = JSON.parse(out)
|
|
290
|
+
document.is_a?(Hash) && document.key?("config_warnings")
|
|
291
|
+
rescue JSON::ParserError
|
|
292
|
+
false
|
|
293
|
+
end
|
|
294
|
+
|
|
224
295
|
# The JSON-RPC channel is the process's stdout, so nothing a command writes past its `out:` (an option
|
|
225
296
|
# parser's `--help`, a stray `puts`) may reach it while the command runs.
|
|
226
297
|
def with_captured_streams(out_io, err_io)
|
|
@@ -235,7 +306,7 @@ module Rigor
|
|
|
235
306
|
end
|
|
236
307
|
|
|
237
308
|
def build_argv(name, args) # rubocop:disable Metrics/MethodLength,Metrics/CyclomaticComplexity,Metrics/AbcSize,Metrics/PerceivedComplexity
|
|
238
|
-
effective_config = args["config"] || @config_path
|
|
309
|
+
effective_config = present_config(args["config"]) || @config_path
|
|
239
310
|
|
|
240
311
|
case name
|
|
241
312
|
when "rigor_check"
|
|
@@ -290,6 +361,14 @@ module Rigor
|
|
|
290
361
|
end
|
|
291
362
|
end
|
|
292
363
|
|
|
364
|
+
# A per-call `config` that is blank names no file, so the session default applies rather than `--config=`
|
|
365
|
+
# reaching the CLI as an empty path (which the CLI refuses).
|
|
366
|
+
def present_config(value)
|
|
367
|
+
return value unless value.is_a?(String) && value.strip.empty?
|
|
368
|
+
|
|
369
|
+
nil
|
|
370
|
+
end
|
|
371
|
+
|
|
293
372
|
def success(id, result)
|
|
294
373
|
{ "jsonrpc" => "2.0", "id" => id, "result" => result }
|
|
295
374
|
end
|
data/lib/rigor/mcp.rb
CHANGED
|
@@ -4,7 +4,7 @@ module Rigor
|
|
|
4
4
|
# The MCP (Model Context Protocol) server subsystem. See `docs/adr/33-mcp-server.md` for the design.
|
|
5
5
|
#
|
|
6
6
|
# Entry point: `rigor mcp --transport stdio`. The server exposes Rigor's analysis tools (check, type-of, triage,
|
|
7
|
-
# annotate, sig-gen, explain, coverage) as MCP tool calls over a newline-delimited JSON-RPC 2.0 stdio stream.
|
|
7
|
+
# lens, annotate, sig-gen, explain, coverage) as MCP tool calls over a newline-delimited JSON-RPC 2.0 stdio stream.
|
|
8
8
|
module MCP
|
|
9
9
|
end
|
|
10
10
|
end
|
|
@@ -6,8 +6,7 @@ module Rigor
|
|
|
6
6
|
# analyzer can convert the exception into a `:plugin_loader` diagnostic without crashing `rigor check`.
|
|
7
7
|
#
|
|
8
8
|
# The policy is documented in [ADR-2 § "Plugin Trust and I/O Policy"](../../../docs/adr/2-extension-api.md).
|
|
9
|
-
#
|
|
10
|
-
# may extend the set.
|
|
9
|
+
# `reason:` is one of `:read_outside_scope`, `:network_disabled`, `:url_fetch_failed` or `:url_body_too_large`.
|
|
11
10
|
class AccessDeniedError < StandardError
|
|
12
11
|
attr_reader :reason, :resource
|
|
13
12
|
|
data/lib/rigor/plugin/base.rb
CHANGED
|
@@ -8,6 +8,7 @@ require "prism"
|
|
|
8
8
|
require_relative "manifest"
|
|
9
9
|
require_relative "node_context"
|
|
10
10
|
require_relative "../analysis/diagnostic"
|
|
11
|
+
require_relative "../analysis/run_disclosure_credit"
|
|
11
12
|
require_relative "../cache/engine_source"
|
|
12
13
|
# `producer generation_cap:` defaults to (and validates against) `Cache::Store::UNBOUNDED_GENERATIONS`, and a
|
|
13
14
|
# plugin class body can be evaluated before anything else pulled the cache layer in.
|
|
@@ -780,11 +781,12 @@ module Rigor
|
|
|
780
781
|
# that differs between workers.
|
|
781
782
|
def disclose_once(key, message:, severity: :info, rule: "load-error")
|
|
782
783
|
id = key.to_s
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
784
|
+
unless run_registration_taken?(id, batch: false)
|
|
785
|
+
@run_disclosures[id] = {
|
|
786
|
+
key: id, message: message.to_s, severity: severity.to_sym, rule: rule.to_s
|
|
787
|
+
}.freeze
|
|
788
|
+
end
|
|
789
|
+
attribute_run_registration(id)
|
|
788
790
|
nil
|
|
789
791
|
end
|
|
790
792
|
|
|
@@ -816,14 +818,15 @@ module Rigor
|
|
|
816
818
|
# raises `ArgumentError`, which the engine reports as this plugin's `runtime-error`.
|
|
817
819
|
def emit_once(key, diagnostics)
|
|
818
820
|
id = key.to_s
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
next row.dup.freeze if row.is_a?(Rigor::Analysis::Diagnostic)
|
|
821
|
+
unless run_registration_taken?(id, batch: true)
|
|
822
|
+
batch = Array(diagnostics).map do |row|
|
|
823
|
+
next row.dup.freeze if row.is_a?(Rigor::Analysis::Diagnostic)
|
|
823
824
|
|
|
824
|
-
|
|
825
|
+
raise ArgumentError, "emit_once expects Rigor::Analysis::Diagnostic rows, got #{row.class}"
|
|
826
|
+
end
|
|
827
|
+
@run_disclosures[id] = { key: id, diagnostics: batch.freeze }.freeze
|
|
825
828
|
end
|
|
826
|
-
|
|
829
|
+
attribute_run_registration(id)
|
|
827
830
|
nil
|
|
828
831
|
end
|
|
829
832
|
|
|
@@ -847,6 +850,18 @@ module Rigor
|
|
|
847
850
|
end
|
|
848
851
|
private :run_registration_taken?
|
|
849
852
|
|
|
853
|
+
# Issue #2020 — hands every call, a repeat of a key already registered included, to the engine's per-file
|
|
854
|
+
# attribution ({Analysis::RunDisclosureCredit}), so an `--incremental` recheck can replay the row for a file
|
|
855
|
+
# it serves from cache. A repeat counts because a discloser memoises the input its notice is about and
|
|
856
|
+
# re-registers the notice from each file's hook. A batch registered empty adds nothing to replay.
|
|
857
|
+
def attribute_run_registration(id)
|
|
858
|
+
record = @run_disclosures[id]
|
|
859
|
+
return if record.nil? || record[:diagnostics]&.empty?
|
|
860
|
+
|
|
861
|
+
Analysis::RunDisclosureCredit.attribute(self, record)
|
|
862
|
+
end
|
|
863
|
+
private :attribute_run_registration
|
|
864
|
+
|
|
850
865
|
# Boilerplate-reduction helper (review §1.3): the "did you mean …?" suggestion every
|
|
851
866
|
# diagnostic-emitting plugin otherwise hand-rolls. Returns the closest of `candidates` to `name` via
|
|
852
867
|
# `DidYouMean::SpellChecker` (the same engine Ruby's own `NoMethodError` hints use), or `nil` when
|
|
@@ -127,7 +127,7 @@ module Rigor
|
|
|
127
127
|
def require_bundled_plugins
|
|
128
128
|
return {} unless File.directory?(BUNDLED_PLUGINS_ROOT)
|
|
129
129
|
|
|
130
|
-
add_bundled_lib_dirs
|
|
130
|
+
Loader.add_bundled_lib_dirs
|
|
131
131
|
Dir.children(BUNDLED_PLUGINS_ROOT).sort.each_with_object({}) do |gem_name, resolved|
|
|
132
132
|
path = Loader.bundled_plugin_path(gem_name)
|
|
133
133
|
next if path.nil?
|
|
@@ -137,17 +137,6 @@ module Rigor
|
|
|
137
137
|
end
|
|
138
138
|
end
|
|
139
139
|
|
|
140
|
-
# A bundled plugin's entry file requires its own tree by bare feature name (`require
|
|
141
|
-
# "rigor/plugin/activestorage"`), which resolves only with that plugin's `lib` on the load path — the
|
|
142
|
-
# loader gets it from the bundle, and this catalogue has to arrange it itself. Prepending the engine's
|
|
143
|
-
# own copies matches ADR-93 WD5's anti-skew rule, where the bundled copy always wins.
|
|
144
|
-
def add_bundled_lib_dirs
|
|
145
|
-
Dir.children(BUNDLED_PLUGINS_ROOT).sort.each do |gem_name|
|
|
146
|
-
dir = File.join(BUNDLED_PLUGINS_ROOT, gem_name, "lib")
|
|
147
|
-
$LOAD_PATH.unshift(dir) if File.directory?(dir) && !$LOAD_PATH.include?(dir)
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
140
|
# A plugin gem that fails to load is skipped rather than fatal: the catalogue exists to *advise*, and
|
|
152
141
|
# a broken bundled copy is already reported by `rigor doctor`'s plugin checks for the plugins the
|
|
153
142
|
# project actually enabled. The failure is recorded in {load_failures} so a missing entry stays
|
|
@@ -17,7 +17,7 @@ module Rigor
|
|
|
17
17
|
# scope and feed the cache descriptor; contributions built on top of out-of-scope reads will not
|
|
18
18
|
# invalidate correctly.
|
|
19
19
|
#
|
|
20
|
-
#
|
|
20
|
+
# The surface:
|
|
21
21
|
#
|
|
22
22
|
# - `#read_file(path)` — validates against the policy, returns the file's contents, and adds a
|
|
23
23
|
# digest-keyed {Cache::Descriptor::FileEntry} to the boundary's accumulated descriptor. A read that
|
data/lib/rigor/plugin/loader.rb
CHANGED
|
@@ -90,6 +90,20 @@ module Rigor
|
|
|
90
90
|
File.directory?(path) ? path : nil
|
|
91
91
|
end
|
|
92
92
|
|
|
93
|
+
# Puts every bundled plugin's `lib` on `$LOAD_PATH`, so a bundled plugin that requires another by gem
|
|
94
|
+
# name (`require "rigor-ffi"` from `rigor-ethon`, `rigor-sassc`, `rigor-ffi-rzmq` and `rigor-rbnacl`)
|
|
95
|
+
# resolves without a bundle. The engine's own copies go first, which matches ADR-93 WD5's anti-skew
|
|
96
|
+
# rule: the bundled copy always wins. Idempotent; a trimmed packaging with no `plugins/` tree adds none.
|
|
97
|
+
def self.add_bundled_lib_dirs
|
|
98
|
+
root = File.join(ENGINE_ROOT, "plugins")
|
|
99
|
+
return unless File.directory?(root)
|
|
100
|
+
|
|
101
|
+
Dir.children(root).sort.each do |gem_name|
|
|
102
|
+
dir = File.join(root, gem_name, "lib")
|
|
103
|
+
$LOAD_PATH.unshift(dir) if File.directory?(dir) && !$LOAD_PATH.include?(dir)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
93
107
|
# @param entries — the raw `plugins:` list from the configuration.
|
|
94
108
|
def load(entries)
|
|
95
109
|
plugins = []
|
|
@@ -233,7 +247,13 @@ module Rigor
|
|
|
233
247
|
# `feature_resolver` is unaffected: an absolute-path require's `$LOADED_FEATURES` entry still ends in
|
|
234
248
|
# `/<gem>.rb`, the suffix {FEATURE_RESOLVER} matches.
|
|
235
249
|
def require_gem!(entry)
|
|
236
|
-
|
|
250
|
+
path = self.class.bundled_plugin_path(entry[:gem])
|
|
251
|
+
if path
|
|
252
|
+
self.class.add_bundled_lib_dirs
|
|
253
|
+
@requirer.call(path)
|
|
254
|
+
else
|
|
255
|
+
@requirer.call(entry[:gem])
|
|
256
|
+
end
|
|
237
257
|
rescue ::LoadError => e
|
|
238
258
|
raise LoadError.new(
|
|
239
259
|
"could not load plugin gem #{entry[:gem].inspect}: #{e.message}",
|
|
@@ -272,13 +292,10 @@ module Rigor
|
|
|
272
292
|
end
|
|
273
293
|
end
|
|
274
294
|
|
|
275
|
-
# A gem that registers
|
|
276
|
-
#
|
|
277
|
-
#
|
|
278
|
-
#
|
|
279
|
-
# which activates the wrong plugin. The failure was load-order dependent (listing `rigor-ffi` first
|
|
280
|
-
# made the nested require a no-op and the entry loaded), so it never showed up in the suite, where
|
|
281
|
-
# some earlier example has always loaded `rigor-ffi`.
|
|
295
|
+
# A gem that registers several plugins is a meta-gem only when NONE of the ids is its own. The FFI family
|
|
296
|
+
# (`rigor-sassc`, `rigor-ethon`, `rigor-ffi-rzmq`, `rigor-rbnacl`) each `require "rigor-ffi"`, so listing
|
|
297
|
+
# one alone registered `ffi` too and was wrongly rejected as a meta-gem. The bug was load-order dependent
|
|
298
|
+
# and hid in the suite, where an earlier example always loaded `rigor-ffi`.
|
|
282
299
|
#
|
|
283
300
|
# `rigor-rails` stays a meta-gem under this rule: it registers eight ids and none of them is `rails`.
|
|
284
301
|
def own_id_among(gem_name, ids)
|
|
@@ -48,11 +48,11 @@ module Rigor
|
|
|
48
48
|
#
|
|
49
49
|
# Slice 2 ships at the **floor**: each emit row's `name:` is the source of truth for the synthetic
|
|
50
50
|
# method's name (a single `"\#{name}"` placeholder gets interpolated with the literal symbol argument at
|
|
51
|
-
# `symbol_arg_position`). The
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
51
|
+
# `symbol_arg_position`). The synthetic method's return type is the `returns_from_arg:` lookup when it
|
|
52
|
+
# resolves, else a non-empty `returns:` string (`SyntheticMethodScanner#resolve_emit_return_type`); the
|
|
53
|
+
# dispatcher turns it into a nominal type through `MethodDispatcher#promote_via_return_type` when the
|
|
54
|
+
# environment knows the class. With neither, an `"untyped"` / `"void"` placeholder, or an unknown class, the
|
|
55
|
+
# method answers `Dynamic[Top]`. Resolution through ADR-13's `Plugin::TypeNodeResolver` is not wired.
|
|
56
56
|
#
|
|
57
57
|
# Engine wiring: `Inference::SyntheticMethodScanner` (slice 2b, `synthetic_method_scanner.rb`) consumes
|
|
58
58
|
# `manifest.heredoc_templates`. Worked consumers: `plugins/rigor-dry-struct/` and
|
|
@@ -98,7 +98,7 @@ module Rigor
|
|
|
98
98
|
# `\#{name}` with the call-site literal symbol) and its declared return type. The return type can be
|
|
99
99
|
# a static String (resolved via `Environment#nominal_for_name` per ADR-16 slice 6b) or a
|
|
100
100
|
# per-call-site lookup ({ReturnsFromArg}) — see
|
|
101
|
-
# [ADR-18](
|
|
101
|
+
# [ADR-18](../../../../docs/adr/18-substrate-per-call-site-return-type.md). When both are nil, the
|
|
102
102
|
# synthesised method's return type falls back to `Dynamic[Top]`.
|
|
103
103
|
class Emit
|
|
104
104
|
attr_reader :name, :returns, :returns_from_arg
|
|
@@ -147,8 +147,7 @@ module Rigor
|
|
|
147
147
|
# lookup_via: { plugin_id: "dry-types", fact: :dry_type_aliases }
|
|
148
148
|
# }
|
|
149
149
|
#
|
|
150
|
-
#
|
|
151
|
-
# extraction + fact-store lookup land in slice 2 / 3.
|
|
150
|
+
# The scanner's `resolve_emit_return_type` does the arg-position extraction and fact-store lookup.
|
|
152
151
|
class ReturnsFromArg
|
|
153
152
|
attr_reader :position, :plugin_id, :fact
|
|
154
153
|
|
|
@@ -39,12 +39,10 @@ module Rigor
|
|
|
39
39
|
# lexical receiver class equals or inherits from this constraint.
|
|
40
40
|
# - `method_name` — Symbol naming the DSL method (e.g. `:devise`).
|
|
41
41
|
# - `symbol_arg_position` — `:rest` (all positional Symbol args are traits, slice 3a's only supported
|
|
42
|
-
# form) or a non-negative Integer (the index of a single trait symbol
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
# `macro.tier_b.unknown-trait` `:info` provenance marker per WD9 / WD13).
|
|
47
|
-
# - `always_included` — Array<String>. Fully-qualified module names that are added to every call site
|
|
42
|
+
# form) or a non-negative Integer (the index of a single trait symbol).
|
|
43
|
+
# - `modules_by_symbol` — Maps each recognised trait symbol to a fully-qualified module name. Symbols not
|
|
44
|
+
# in the table are silently skipped.
|
|
45
|
+
# - `always_included` — Fully-qualified module names that are added to every call site
|
|
48
46
|
# (even when no symbols match). Mirrors Devise's `always_include` modules.
|
|
49
47
|
#
|
|
50
48
|
# ## Floor / ceiling per ADR-16 WD13
|
|
@@ -53,7 +51,7 @@ module Rigor
|
|
|
53
51
|
# methods into the existing `SyntheticMethodIndex` (slice 2b primitive). The synthesised methods adopt
|
|
54
52
|
# the module's authored RBS return types — Tier B is NOT subject to the Tier C `Dynamic[T]` floor
|
|
55
53
|
# because the source-of-truth (the module's authored RBS) is not a manifest-declared string. Per ADR-5
|
|
56
|
-
# robustness, the substrate does not fabricate precision; it simply replays the
|
|
54
|
+
# robustness, the substrate does not fabricate precision; it simply replays the module's signatures.
|
|
57
55
|
#
|
|
58
56
|
# **Out of scope for slice 3** (deferred follow-ups):
|
|
59
57
|
# - `class_methods_module:` per-trait (Devise's `ClassMethods` extend-pattern); slice 3 covers instance
|
|
@@ -106,8 +104,7 @@ module Rigor
|
|
|
106
104
|
end
|
|
107
105
|
|
|
108
106
|
# @return fully-qualified module name for the given trait symbol, or nil when the
|
|
109
|
-
# registry doesn't know the symbol (caller
|
|
110
|
-
# through).
|
|
107
|
+
# registry doesn't know the symbol (the caller skips it).
|
|
111
108
|
def module_for(symbol)
|
|
112
109
|
modules_by_symbol[symbol.to_sym]
|
|
113
110
|
end
|
|
@@ -56,7 +56,7 @@ module Rigor
|
|
|
56
56
|
:hkt_registrations, :hkt_definitions, :signature_paths, :protocol_contracts,
|
|
57
57
|
:source_rbs_synthesizer, :additional_initializers,
|
|
58
58
|
:effect_root, :effect_labels, :effect_attributions, :effect_edges, :effect_entry_points,
|
|
59
|
-
:effect_ancestry
|
|
59
|
+
:effect_ancestry, :per_invocation_classes, :per_invocation_non_action_macros
|
|
60
60
|
|
|
61
61
|
# #392 — the project-relative globs whose files this plugin compiles into {TemplateUnit}s. Purely a
|
|
62
62
|
# CLAIM: the engine globs these, reads each match's bytes and offers them to
|
|
@@ -74,7 +74,8 @@ module Rigor
|
|
|
74
74
|
hkt_registrations: [], hkt_definitions: [], signature_paths: [], protocol_contracts: [],
|
|
75
75
|
source_rbs_synthesizer: nil, additional_initializers: [],
|
|
76
76
|
effect_root: nil, effect_labels: [], effect_attributions: [], effect_edges: [],
|
|
77
|
-
effect_entry_points: [], effect_ancestry: [], template_globs: []
|
|
77
|
+
effect_entry_points: [], effect_ancestry: [], template_globs: [], per_invocation_classes: [],
|
|
78
|
+
per_invocation_non_action_macros: []
|
|
78
79
|
)
|
|
79
80
|
validate_id!(id)
|
|
80
81
|
validate_version!(version)
|
|
@@ -109,6 +110,7 @@ module Rigor
|
|
|
109
110
|
assign_additional_initializers(additional_initializers)
|
|
110
111
|
assign_effect_fields(effect_root, effect_labels, effect_attributions, effect_edges,
|
|
111
112
|
effect_entry_points, effect_ancestry, template_globs)
|
|
113
|
+
assign_per_invocation_classes(per_invocation_classes, per_invocation_non_action_macros)
|
|
112
114
|
freeze
|
|
113
115
|
end
|
|
114
116
|
|
|
@@ -166,9 +168,8 @@ module Rigor
|
|
|
166
168
|
end
|
|
167
169
|
private :assign_additional_initializers
|
|
168
170
|
|
|
169
|
-
# ADR-103 WD2 / WD6 / WD10 / WD14 — the
|
|
170
|
-
#
|
|
171
|
-
# The six `effect_*` fields validate together, so {#initialize} carries one call rather than six.
|
|
171
|
+
# ADR-103 WD2 / WD6 / WD10 / WD14 — the six `effect_*` fields validate together here, so {#initialize}
|
|
172
|
+
# carries one call rather than six.
|
|
172
173
|
def validate_effect_fields!(effect_root, effect_labels, effect_attributions, effect_edges,
|
|
173
174
|
effect_entry_points, effect_ancestry)
|
|
174
175
|
validate_effect_root!(effect_root)
|
|
@@ -202,6 +203,28 @@ module Rigor
|
|
|
202
203
|
@template_globs = globs.map { |glob| glob.to_s.dup.freeze }.uniq.freeze
|
|
203
204
|
end
|
|
204
205
|
private :assign_template_globs
|
|
206
|
+
|
|
207
|
+
# ADR-125 — `per_invocation_classes:` names the framework classes whose every instance serves exactly one
|
|
208
|
+
# entry point, so ivar writes in two distinct entry points of a descendant are never live together.
|
|
209
|
+
# `per_invocation_non_action_macros:` names the class-body macros whose Symbol, String or inline-`def`
|
|
210
|
+
# arguments name a public method that is NOT an entry point (`before_action :load`, `helper_method
|
|
211
|
+
# :current_user`); they apply to this manifest's own `per_invocation_classes:`, so a list without classes
|
|
212
|
+
# is refused. Validated and assigned in one call, because `#initialize` is at its line budget.
|
|
213
|
+
def assign_per_invocation_classes(names, macros)
|
|
214
|
+
validate_array_of!("per_invocation_classes", names, "non-empty String") do |name|
|
|
215
|
+
name.is_a?(String) && !name.empty?
|
|
216
|
+
end
|
|
217
|
+
validate_array_of!("per_invocation_non_action_macros", macros, "Symbol/non-empty String") do |macro|
|
|
218
|
+
macro.is_a?(Symbol) || (macro.is_a?(String) && !macro.empty?)
|
|
219
|
+
end
|
|
220
|
+
if names.empty? && !macros.empty?
|
|
221
|
+
raise ArgumentError, "per_invocation_non_action_macros needs per_invocation_classes to apply to"
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
@per_invocation_classes = names.map { |name| name.to_s.delete_prefix("::").dup.freeze }.uniq.freeze
|
|
225
|
+
@per_invocation_non_action_macros = macros.map(&:to_sym).uniq.freeze
|
|
226
|
+
end
|
|
227
|
+
private :assign_per_invocation_classes
|
|
205
228
|
# rubocop:enable Metrics/ParameterLists, Metrics/AbcSize
|
|
206
229
|
|
|
207
230
|
public
|
|
@@ -258,7 +281,9 @@ module Rigor
|
|
|
258
281
|
"effect_edges" => effect_edges.map(&:to_h),
|
|
259
282
|
"effect_entry_points" => effect_entry_points.map(&:to_h),
|
|
260
283
|
"effect_ancestry" => effect_ancestry.map(&:to_h),
|
|
261
|
-
"template_globs" => template_globs
|
|
284
|
+
"template_globs" => template_globs,
|
|
285
|
+
"per_invocation_classes" => per_invocation_classes,
|
|
286
|
+
"per_invocation_non_action_macros" => per_invocation_non_action_macros.map(&:to_s)
|
|
262
287
|
}
|
|
263
288
|
end
|
|
264
289
|
|
|
@@ -449,10 +474,9 @@ module Rigor
|
|
|
449
474
|
end
|
|
450
475
|
end
|
|
451
476
|
|
|
452
|
-
# ADR-13
|
|
453
|
-
#
|
|
454
|
-
#
|
|
455
|
-
# registration order; the parser integration that actually drives the chain lands in slice 3.
|
|
477
|
+
# ADR-13 — `type_node_resolvers:` declares the plugin-supplied `TypeNodeResolver` instances the parser
|
|
478
|
+
# consults when an RBS::Extended payload's named- or generic-type head misses the built-in registry. The
|
|
479
|
+
# registry exposes them in registration order, and `Environment#build_name_scope` builds the chain.
|
|
456
480
|
def validate_type_node_resolvers!(resolvers)
|
|
457
481
|
validate_array_of!("type_node_resolvers", resolvers, "Rigor::Plugin::TypeNodeResolver instances") do |r|
|
|
458
482
|
r.is_a?(TypeNodeResolver)
|
|
@@ -460,8 +484,7 @@ module Rigor
|
|
|
460
484
|
end
|
|
461
485
|
|
|
462
486
|
# ADR-16 slice 1a — `block_as_methods:` declares the Tier A substrate entries the plugin contributes.
|
|
463
|
-
#
|
|
464
|
-
# `Scope#self_type` for matching blocks arrives in a subsequent slice.
|
|
487
|
+
# The engine hook that narrows `Scope#self_type` for matching blocks is `Inference::MacroBlockSelfType`.
|
|
465
488
|
def validate_block_as_methods!(entries)
|
|
466
489
|
validate_array_of!("block_as_methods", entries, "Rigor::Plugin::Macro::BlockAsMethod instances") do |e|
|
|
467
490
|
e.is_a?(Macro::BlockAsMethod)
|
|
@@ -469,8 +492,7 @@ module Rigor
|
|
|
469
492
|
end
|
|
470
493
|
|
|
471
494
|
# ADR-16 slice 2a — `heredoc_templates:` declares the Tier C substrate entries (heredoc-template
|
|
472
|
-
# synthesis on class-level DSL calls).
|
|
473
|
-
# `SyntheticMethodIndex` that actually emit synthetic methods arrive in slice 2b.
|
|
495
|
+
# synthesis on class-level DSL calls). `Inference::SyntheticMethodScanner` emits the synthetic methods.
|
|
474
496
|
def validate_heredoc_templates!(entries)
|
|
475
497
|
validate_array_of!("heredoc_templates", entries, "Rigor::Plugin::Macro::HeredocTemplate instances") do |e|
|
|
476
498
|
e.is_a?(Macro::HeredocTemplate)
|
|
@@ -489,8 +511,8 @@ module Rigor
|
|
|
489
511
|
end
|
|
490
512
|
|
|
491
513
|
# ADR-16 slice 3a — `trait_registries:` declares the Tier B substrate entries (trait-inlining via
|
|
492
|
-
# bundled module registry).
|
|
493
|
-
#
|
|
514
|
+
# bundled module registry). `Inference::SyntheticMethodScanner#collect_trait_registries` explodes them
|
|
515
|
+
# per method through `SyntheticMethodIndex`.
|
|
494
516
|
def validate_trait_registries!(entries)
|
|
495
517
|
validate_array_of!("trait_registries", entries, "Rigor::Plugin::Macro::TraitRegistry instances") do |e|
|
|
496
518
|
e.is_a?(Macro::TraitRegistry)
|
|
@@ -214,7 +214,7 @@ module Rigor
|
|
|
214
214
|
# tie-breaker.
|
|
215
215
|
#
|
|
216
216
|
# ADR-15 Phase 3 — alongside the instantiated `plugins`, the registry carries `blueprints`: a frozen,
|
|
217
|
-
# Ractor-shareable
|
|
217
|
+
# Ractor-shareable list of {Blueprint} that records how to re-instantiate the same plugin set in a worker
|
|
218
218
|
# Ractor. The eventual Phase 4 pool ships `blueprints` across the boundary and calls {.materialize}
|
|
219
219
|
# per-Ractor; the live `plugins` carriage on the coordinator registry stays unchanged.
|
|
220
220
|
class Registry
|
|
@@ -397,6 +397,28 @@ module Rigor
|
|
|
397
397
|
@rbs_complete_extends_set.include?(class_name.to_s)
|
|
398
398
|
end
|
|
399
399
|
|
|
400
|
+
# ADR-125 — the aggregate class names declared across loaded plugins as per-invocation (manifest
|
|
401
|
+
# `per_invocation_classes:`): each instance of a descendant serves exactly one public-method entry point.
|
|
402
|
+
# `per_invocation_class?` is the exact-name membership predicate; `def.ivar-write-mismatch` walks a
|
|
403
|
+
# class's ancestry and asks it of each ancestor.
|
|
404
|
+
attr_reader :per_invocation_classes
|
|
405
|
+
|
|
406
|
+
def per_invocation_class?(class_name)
|
|
407
|
+
return false if class_name.nil?
|
|
408
|
+
|
|
409
|
+
@per_invocation_classes_set.include?(class_name.to_s.delete_prefix("::"))
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
# ADR-125 — the macros whose arguments name a non-entry-point method on a descendant of the declared
|
|
413
|
+
# per-invocation class `class_name`: the union of `per_invocation_non_action_macros:` over the manifests
|
|
414
|
+
# that declare that class. An empty Set for a class no manifest declares.
|
|
415
|
+
def per_invocation_non_action_macros(class_name)
|
|
416
|
+
@per_invocation_macros.fetch(class_name.to_s.delete_prefix("::"), EMPTY_MACROS)
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
EMPTY_MACROS = Set.new.freeze
|
|
420
|
+
private_constant :EMPTY_MACROS
|
|
421
|
+
|
|
400
422
|
# ADR-28 — flat, ordered list of every loaded plugin's path-scoped method-protocol contracts, in
|
|
401
423
|
# plugin registration order. Read from each plugin's `#protocol_contracts` (which the manifest backs
|
|
402
424
|
# by default but a plugin MAY override to fold in per-project config). Consumed by
|
|
@@ -465,12 +487,28 @@ module Rigor
|
|
|
465
487
|
@rbs_complete_ancestors_set = @rbs_complete_ancestors.to_set.freeze
|
|
466
488
|
@rbs_complete_extends = aggregate_class_names(manifests, :rbs_complete_extends)
|
|
467
489
|
@rbs_complete_extends_set = @rbs_complete_extends.to_set.freeze
|
|
490
|
+
@per_invocation_classes = aggregate_class_names(manifests, :per_invocation_classes)
|
|
491
|
+
@per_invocation_classes_set = @per_invocation_classes.to_set.freeze
|
|
492
|
+
@per_invocation_macros = compile_per_invocation_macros(manifests)
|
|
468
493
|
@type_node_resolvers = manifests.flat_map { |m| m&.type_node_resolvers || [] }.freeze
|
|
469
494
|
@protocol_contracts = @plugins.flat_map { |p| safe_protocol_contracts(p) }.freeze
|
|
470
495
|
@contracts_by_path = {}
|
|
471
496
|
@effect_memo = {}
|
|
472
497
|
end
|
|
473
498
|
|
|
499
|
+
# ADR-125 — declared class name => frozen Set of the non-action macros its declaring manifests list.
|
|
500
|
+
def compile_per_invocation_macros(manifests)
|
|
501
|
+
table = Hash.new { |hash, key| hash[key] = Set.new }
|
|
502
|
+
manifests.each do |manifest|
|
|
503
|
+
next if manifest.nil?
|
|
504
|
+
|
|
505
|
+
manifest.per_invocation_classes.each do |name|
|
|
506
|
+
table[name].merge(manifest.per_invocation_non_action_macros)
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
table.to_h { |name, macros| [name, macros.freeze] }.freeze
|
|
510
|
+
end
|
|
511
|
+
|
|
474
512
|
# The shared shape of `open_receivers:` / `rbs_complete_ancestors:` — a flat uniq'd class-name list
|
|
475
513
|
# aggregated across manifests, each entry normalised to String.
|
|
476
514
|
def aggregate_class_names(manifests, field)
|
|
@@ -518,8 +556,8 @@ module Rigor
|
|
|
518
556
|
# Issue #806 — {#safe_manifest} with a receipt. The construction-time aggregates degraded silently
|
|
519
557
|
# when a manifest read raised, so a plugin that contributed nothing looked identical to a plugin that
|
|
520
558
|
# was never asked; the load-error channel is where "a plugin could not be loaded" already lives, and
|
|
521
|
-
# `Analysis::Runner` turns each entry into one `
|
|
522
|
-
# plugin ref is the CLASS, not `manifest.id`: the manifest is the surface that just failed.
|
|
559
|
+
# `Analysis::Runner` turns each entry into one `load-error` row (source family `plugin_loader`) naming
|
|
560
|
+
# the plugin. The plugin ref is the CLASS, not `manifest.id`: the manifest is the surface that just failed.
|
|
523
561
|
def guarded_manifest(plugin, errors)
|
|
524
562
|
plugin.manifest
|
|
525
563
|
rescue StandardError => e
|
|
@@ -25,9 +25,8 @@ module Rigor
|
|
|
25
25
|
# documents the trust model the boundary enforces.
|
|
26
26
|
#
|
|
27
27
|
# ADR-9 slice 2 adds `fact_store`: the per-run cross-plugin `Plugin::FactStore`. Producer plugins publish
|
|
28
|
-
# their facts in `#prepare(services)
|
|
29
|
-
# `services.fact_store.read(...)`. A fresh `FactStore`
|
|
30
|
-
# supplied — the runner threads its own instance in once slice 3 wires `#prepare` invocation.
|
|
28
|
+
# their facts in `#prepare(services)`; consumer plugins read in `#diagnostics_for_file` via
|
|
29
|
+
# `services.fact_store.read(...)`. A fresh `FactStore` is constructed when none is supplied.
|
|
31
30
|
class Services
|
|
32
31
|
attr_reader :reflection, :type, :configuration, :cache_store, :trust_policy, :fact_store
|
|
33
32
|
|
|
@@ -61,6 +61,17 @@ module Rigor
|
|
|
61
61
|
def empty?
|
|
62
62
|
@entries.empty?
|
|
63
63
|
end
|
|
64
|
+
|
|
65
|
+
# The pool drain ({Rigor::Analysis::WorkerSession#drain_reporters}): the frozen entries.
|
|
66
|
+
def snapshot
|
|
67
|
+
entries
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Replays one worker's {#snapshot}, `kind:` (#824) and `line:` (#1075) included; the dedup above
|
|
71
|
+
# collapses every worker's copy. Nil records nothing.
|
|
72
|
+
def absorb(snapshot)
|
|
73
|
+
Array(snapshot).each { |entry| record(**entry.to_h) }
|
|
74
|
+
end
|
|
64
75
|
end
|
|
65
76
|
end
|
|
66
77
|
end
|
|
@@ -19,8 +19,8 @@ module Rigor
|
|
|
19
19
|
# - `trusted_gems`: gem names the user has authorised. Derived from the `plugins:` section of `.rigor.yml`
|
|
20
20
|
# plus any gems they reach transitively. Used today for documentation and future trust diagnostics.
|
|
21
21
|
# - `allowed_read_roots`: absolute paths plugin code may read from through the {IoBoundary}. The default
|
|
22
|
-
# set covers the project root, the project's `signature_paths`,
|
|
23
|
-
#
|
|
22
|
+
# set covers the project root, the project's `signature_paths`, and each trusted
|
|
23
|
+
# gem's `Gem::Specification#full_gem_path`. The user extends this with `.rigor.yml`'s
|
|
24
24
|
# `plugins_io.allowed_paths:`.
|
|
25
25
|
# - `network_policy`: one of {VALID_NETWORK_POLICIES}. `:disabled` (default) makes
|
|
26
26
|
# {IoBoundary#open_url} always raise. `:allowlist` (v0.1.2) consults `allowed_url_hosts` on every fetch —
|