rigortype 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -14
  3. data/docs/handbook/01-getting-started.md +311 -0
  4. data/docs/handbook/02-everyday-types.md +337 -0
  5. data/docs/handbook/03-narrowing.md +359 -0
  6. data/docs/handbook/04-tuples-and-shapes.md +321 -0
  7. data/docs/handbook/05-methods-and-blocks.md +339 -0
  8. data/docs/handbook/06-classes.md +305 -0
  9. data/docs/handbook/07-rbs-and-extended.md +427 -0
  10. data/docs/handbook/08-understanding-errors.md +373 -0
  11. data/docs/handbook/09-plugins.md +241 -0
  12. data/docs/handbook/10-sorbet.md +347 -0
  13. data/docs/handbook/11-sig-gen.md +312 -0
  14. data/docs/handbook/12-lightweight-hkt.md +333 -0
  15. data/docs/handbook/README.md +275 -0
  16. data/docs/handbook/appendix-elixir.md +370 -0
  17. data/docs/handbook/appendix-go.md +399 -0
  18. data/docs/handbook/appendix-java-csharp.md +470 -0
  19. data/docs/handbook/appendix-liskov.md +580 -0
  20. data/docs/handbook/appendix-mypy.md +370 -0
  21. data/docs/handbook/appendix-phpstan.md +338 -0
  22. data/docs/handbook/appendix-protocols-and-structural-typing.md +292 -0
  23. data/docs/handbook/appendix-rust.md +446 -0
  24. data/docs/handbook/appendix-steep.md +336 -0
  25. data/docs/handbook/appendix-type-theory.md +1662 -0
  26. data/docs/handbook/appendix-typeprof.md +416 -0
  27. data/docs/handbook/appendix-typescript.md +332 -0
  28. data/docs/install.md +189 -0
  29. data/docs/llms.txt +72 -0
  30. data/docs/manual/01-installation.md +342 -0
  31. data/docs/manual/02-cli-reference.md +569 -0
  32. data/docs/manual/03-configuration.md +152 -0
  33. data/docs/manual/04-diagnostics.md +206 -0
  34. data/docs/manual/05-inspecting-types.md +109 -0
  35. data/docs/manual/06-baseline.md +104 -0
  36. data/docs/manual/07-plugins.md +92 -0
  37. data/docs/manual/08-skills.md +143 -0
  38. data/docs/manual/09-editor-integration.md +245 -0
  39. data/docs/manual/10-mcp-server.md +539 -0
  40. data/docs/manual/11-ci.md +274 -0
  41. data/docs/manual/12-caching.md +116 -0
  42. data/docs/manual/13-troubleshooting.md +120 -0
  43. data/docs/manual/14-rails-quickstart.md +332 -0
  44. data/docs/manual/15-type-protection-coverage.md +204 -0
  45. data/docs/manual/16-rbs-extended-annotations.md +190 -0
  46. data/docs/manual/17-driving-improvement.md +160 -0
  47. data/docs/manual/README.md +87 -0
  48. data/docs/manual/ci-templates/README.md +58 -0
  49. data/docs/manual/plugins/README.md +86 -0
  50. data/docs/manual/plugins/rigor-actioncable.md +78 -0
  51. data/docs/manual/plugins/rigor-actionmailer.md +74 -0
  52. data/docs/manual/plugins/rigor-actionpack.md +80 -0
  53. data/docs/manual/plugins/rigor-activejob.md +58 -0
  54. data/docs/manual/plugins/rigor-activerecord.md +102 -0
  55. data/docs/manual/plugins/rigor-activestorage.md +74 -0
  56. data/docs/manual/plugins/rigor-activesupport-core-ext.md +86 -0
  57. data/docs/manual/plugins/rigor-devise.md +70 -0
  58. data/docs/manual/plugins/rigor-dry-schema.md +56 -0
  59. data/docs/manual/plugins/rigor-dry-struct.md +60 -0
  60. data/docs/manual/plugins/rigor-dry-types.md +59 -0
  61. data/docs/manual/plugins/rigor-dry-validation.md +62 -0
  62. data/docs/manual/plugins/rigor-factorybot.md +76 -0
  63. data/docs/manual/plugins/rigor-graphql.md +89 -0
  64. data/docs/manual/plugins/rigor-hanami.md +83 -0
  65. data/docs/manual/plugins/rigor-mangrove.md +73 -0
  66. data/docs/manual/plugins/rigor-minitest.md +86 -0
  67. data/docs/manual/plugins/rigor-pundit.md +72 -0
  68. data/docs/manual/plugins/rigor-rails-i18n.md +92 -0
  69. data/docs/manual/plugins/rigor-rails-routes.md +94 -0
  70. data/docs/manual/plugins/rigor-rails.md +44 -0
  71. data/docs/manual/plugins/rigor-rbs-inline.md +83 -0
  72. data/docs/manual/plugins/rigor-rspec-rails.md +72 -0
  73. data/docs/manual/plugins/rigor-rspec.md +86 -0
  74. data/docs/manual/plugins/rigor-shoulda-matchers.md +78 -0
  75. data/docs/manual/plugins/rigor-sidekiq.md +78 -0
  76. data/docs/manual/plugins/rigor-sinatra.md +61 -0
  77. data/docs/manual/plugins/rigor-sorbet.md +63 -0
  78. data/docs/manual/plugins/rigor-statesman.md +75 -0
  79. data/docs/manual/plugins/rigor-typescript-utility-types.md +71 -0
  80. data/exe/rigor +1 -1
  81. data/lib/rigor/analysis/incremental_session.rb +4 -2
  82. data/lib/rigor/analysis/run_stats.rb +13 -1
  83. data/lib/rigor/analysis/runner.rb +54 -12
  84. data/lib/rigor/cli/check_command.rb +1 -1
  85. data/lib/rigor/cli/docs_command.rb +248 -0
  86. data/lib/rigor/cli/skill_command.rb +103 -41
  87. data/lib/rigor/cli/skill_describe.rb +346 -0
  88. data/lib/rigor/cli/triage_command.rb +8 -2
  89. data/lib/rigor/cli/triage_renderer.rb +4 -0
  90. data/lib/rigor/cli.rb +25 -3
  91. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +124 -32
  92. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +37 -6
  93. data/lib/rigor/inference/scope_indexer.rb +87 -89
  94. data/lib/rigor/plugin/isolation.rb +5 -5
  95. data/lib/rigor/plugin/loader.rb +4 -2
  96. data/lib/rigor/triage/catalogue.rb +16 -1
  97. data/lib/rigor/triage.rb +30 -7
  98. data/lib/rigor/version.rb +1 -1
  99. data/skills/rigor-ask/SKILL.md +172 -0
  100. data/skills/rigor-doctor/SKILL.md +87 -0
  101. data/skills/rigor-editor-setup/SKILL.md +114 -0
  102. data/skills/rigor-mcp-setup/SKILL.md +117 -0
  103. data/skills/rigor-monkeypatch-resolve/SKILL.md +79 -0
  104. data/skills/rigor-next-steps/SKILL.md +113 -0
  105. data/skills/rigor-plugin-tune/SKILL.md +79 -0
  106. data/skills/rigor-protection-uplift/SKILL.md +133 -0
  107. data/skills/rigor-rbs-setup/SKILL.md +128 -0
  108. data/skills/rigor-upgrade/SKILL.md +79 -0
  109. metadata +90 -1
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: rigor-monkeypatch-resolve
3
+ description: |
4
+ Resolve a cluster of `call.unresolved-toplevel` / `call.undefined-method` diagnostics that are really the project's own monkey-patches (core-extension files that add methods with literal `def`) by wiring them into `pre_eval:` so Rigor pre-evaluates them and learns the added methods. Triggers: "Rigor flags methods my core-ext adds", "undefined-method on my own monkey-patch", "set up pre_eval", "lots of unresolved-toplevel from my lib/core_ext". NOT for dynamically-generated methods (define_method / method_missing / class_eval heredocs) — those need a plugin (use rigor-plugin-author) — and NOT for an external gem with no RBS (use rigor-rbs-setup).
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.1.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Rigor Monkeypatch Resolve
12
+
13
+ When a project adds methods to existing classes in its own files — the
14
+ classic `lib/core_ext/*.rb` `class String; def squish; …; end` pattern —
15
+ Rigor does not see them unless told to, so every call to the added method
16
+ fires `call.undefined-method` (or `call.unresolved-toplevel` for a
17
+ top-level helper). `pre_eval:` ([ADR-17](https://github.com/rigortype/rigor/blob/master/docs/adr/17-monkey-patch-pre-evaluation.md))
18
+ fixes this: it pre-evaluates the listed project files and registers every
19
+ method they define with a **literal** `def` / `def self.`.
20
+
21
+ ## When to use
22
+
23
+ - `rigor triage` shows a cluster of `call.undefined-method` /
24
+ `call.unresolved-toplevel` whose receiver is a core class the project
25
+ monkey-patches (or a top-level helper the project defines).
26
+
27
+ ## When NOT to use
28
+
29
+ - The methods are **generated dynamically** (`define_method` with a
30
+ computed name, `method_missing`, a `class_eval <<~RUBY … def #{name}`
31
+ heredoc). `pre_eval:` walks for *literal* `def`s and will not find
32
+ them — that residue is a `rigor-plugin-author` job.
33
+ - The cluster is on a **third-party gem** with no RBS — that is
34
+ `rigor-rbs-setup`.
35
+
36
+ ## Procedure
37
+
38
+ ### Phase 1 — confirm it is a monkey-patch cluster
39
+
40
+ ```sh
41
+ rigor triage --format json
42
+ ```
43
+
44
+ Look at the `selectors` / `hotspots`: a cluster keyed on a core receiver
45
+ (`String#squish`, `Integer#…`) or a top-level helper, all pointing back
46
+ to a file in your project that defines them. Open that file and confirm
47
+ the methods are written as literal `def` / `def self.`.
48
+
49
+ ### Phase 2 — wire the file(s) into `pre_eval:`
50
+
51
+ Add the defining file paths to `pre_eval:` in `.rigor.dist.yml` (paths
52
+ are resolved relative to the config file):
53
+
54
+ ```yaml
55
+ pre_eval:
56
+ - lib/core_ext/string.rb
57
+ - lib/core_ext/integer.rb
58
+ ```
59
+
60
+ List the **files that contain the `def`s**, not the call sites.
61
+
62
+ ### Phase 3 — verify the cluster cleared
63
+
64
+ ```sh
65
+ rigor check
66
+ ```
67
+
68
+ The `call.undefined-method` cluster on those methods should be gone. If
69
+ some survive, those specific methods are almost certainly
70
+ **dynamically generated** — `pre_eval:` cannot reach them, and the
71
+ durable fix is a project plugin (hand off to `rigor-plugin-author`). A
72
+ genuine typo (a real undefined method) correctly keeps firing — do not
73
+ add it to `pre_eval:`.
74
+
75
+ ## Next step
76
+
77
+ Re-run `rigor skill describe`. With the monkey-patch noise gone, the
78
+ remaining diagnostics are a cleaner target for `rigor-baseline-reduce`
79
+ or `rigor-protection-uplift`.
@@ -0,0 +1,113 @@
1
+ ---
2
+ name: rigor-next-steps
3
+ description: |
4
+ Route a project to its next Rigor step from a single entry point: resolve the `rigor` command (install it if missing), onboard the project if it has no Rigor config, then ask `rigor skill describe` what to do next and hand off to the matching skill. Triggers: "what should we do next with Rigor?", "I want to use Rigor on this project", "help me move this project forward with Rigor", "where do I start with Rigor?". NOT a replacement for the task-specific skills it routes to — it hands off to them.
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.1.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Rigor Next Steps
12
+
13
+ The single entry point for "what should we do next with Rigor on this
14
+ project?" This skill is deliberately **thin**: it only does the
15
+ bootstrap that must happen before `rigor` can speak for itself, then it
16
+ hands the decision to the installed `rigor` binary via
17
+ `rigor skill describe`. That keeps the advice **current to the
18
+ installed version** — the routing intelligence lives in the gem, not
19
+ frozen in this file (ADR-73).
20
+
21
+ Work top to bottom. Stop at the first step that is not yet satisfied,
22
+ complete it, then continue.
23
+
24
+ ## Step 1 — Resolve the `rigor` command
25
+
26
+ ```sh
27
+ rigor --version
28
+ ```
29
+
30
+ If it prints a version (`rigor 0.x.y`), `rigor` is installed — go to
31
+ **Step 3**. If the command is not found, go to **Step 2**.
32
+
33
+ ## Step 2 — Install Rigor (only if Step 1 failed)
34
+
35
+ Rigor is a standalone tool, **not** a project dependency — do not add it
36
+ to the `Gemfile`. Fetch and follow the install guide, which is written
37
+ for an AI agent and picks the right channel (mise / asdf / gem / Docker)
38
+ for the environment:
39
+
40
+ - <https://raw.githubusercontent.com/rigortype/rigor/master/docs/install.md>
41
+
42
+ Follow it until `rigor --version` succeeds, then continue to Step 3.
43
+
44
+ (This is the one step that cannot be driven by `rigor skill describe` —
45
+ there is no binary to ask yet. It is the only install knowledge this
46
+ skill carries, kept as a pointer rather than a copy so it does not go
47
+ stale.)
48
+
49
+ ## Step 3 — Set the project up if it has no Rigor config
50
+
51
+ Check the project root for `.rigor.yml` or `.rigor.dist.yml`. If
52
+ **neither** exists, the project has never been onboarded — set it up
53
+ first:
54
+
55
+ ```sh
56
+ rigor skill rigor-project-init
57
+ ```
58
+
59
+ Follow that skill's body top to bottom (it detects the stack, picks
60
+ plugins, writes `.rigor.dist.yml`, and snapshots a baseline if needed),
61
+ then come back to Step 4.
62
+
63
+ ## Step 4 — Ask `rigor skill describe` what to do next
64
+
65
+ Once `rigor` is installed and the project is configured, let the live
66
+ command pick the next move:
67
+
68
+ ```sh
69
+ rigor skill describe
70
+ ```
71
+
72
+ Its output reports the project's current state (config / baseline /
73
+ `sig/` / CI), names a **recommended next skill** with a one-line reason,
74
+ and lists every skill you can run next with its current description.
75
+
76
+ ## Step 5 — Route and act
77
+
78
+ Read the `## Recommended next step` and `## For the agent` sections of
79
+ the `describe` output, then:
80
+
81
+ - **Present the recommendation to the user proactively** — e.g. "Rigor
82
+ is set up but not wired into CI; I suggest the `rigor-ci-setup` skill
83
+ to lock in the regression guard. Shall I?" — rather than only asking
84
+ an open question.
85
+ - If the user has a different goal, ask which skill they want from the
86
+ listed catalog.
87
+ - Then load the chosen skill and follow it:
88
+
89
+ ```sh
90
+ rigor skill <chosen-skill>
91
+ ```
92
+
93
+ Re-run `rigor skill describe` any time you want the next step again — it
94
+ always reflects the project's current state, so this skill stays useful
95
+ across the whole adoption journey, not just the first run.
96
+
97
+ ## Reading the docs offline
98
+
99
+ Once Rigor is installed (Step 1 succeeded), the full manual **and
100
+ handbook** ship inside the gem — read any chapter **without the
101
+ network**:
102
+
103
+ ```sh
104
+ rigor docs # the offline doc index (what's available)
105
+ rigor docs <name> # a chapter, e.g. `rigor docs editor-integration`
106
+ rigor docs --list # name + absolute path for every bundled doc
107
+ rigor docs --list handbook # just the handbook chapters
108
+ ```
109
+
110
+ Prefer this over fetching the same page from the web: it is the exact
111
+ copy that shipped with the installed `rigor`, so it always matches the
112
+ binary's behaviour. (The web copy at <https://rigor.typedduck.fail/llms.txt>
113
+ is for the contributor-facing corpus and for reading before install.)
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: rigor-plugin-tune
3
+ description: |
4
+ Re-scan a configured project's Gemfile.lock against Rigor's bundled plugin catalogue and enable the plugins that match its current dependencies (Rails, RSpec, dry-rb, Sidekiq, Devise, …), then verify they all load. Run it after adding a gem, or when an onboarding predates a dependency the project now uses. Triggers: "enable the right Rigor plugins", "I added gem X, does Rigor have a plugin?", "which rigor plugins should this project use?", "rigor plugins for my stack". NOT for first-time onboarding (use rigor-project-init, which does the initial selection) and NOT for authoring a new plugin (use rigor-plugin-author).
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.1.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Rigor Plugin Tune
12
+
13
+ `rigor-project-init` selects plugins once, at onboarding. Dependencies
14
+ drift afterward — a project adds Sidekiq, adopts dry-rb, brings in
15
+ shoulda-matchers — and the bundled Rigor plugin that understands the new
16
+ gem is not enabled until someone wires it. This skill is the recurring
17
+ "re-match plugins to the current Gemfile.lock" pass.
18
+
19
+ All `rigor-*` plugins ship **bundled inside the `rigortype` gem** — no
20
+ separate install. Enabling one is just adding its id to `plugins:` in
21
+ `.rigor.dist.yml`.
22
+
23
+ ## When to use
24
+
25
+ - The project added (or removed) a gem since it was onboarded.
26
+ - `rigor check` is missing framework knowledge (e.g. ActiveRecord
27
+ relation methods, RSpec matchers) that a bundled plugin would supply.
28
+
29
+ ## When NOT to use
30
+
31
+ - First-time setup — `rigor-project-init` does the initial selection.
32
+ - A gem with **no** bundled plugin and no RBS — that is
33
+ `rigor-rbs-setup` (community RBS) or, for your own DSL,
34
+ `rigor-plugin-author`.
35
+
36
+ ## Procedure
37
+
38
+ ### Phase 1 — list what is enabled vs what is installed
39
+
40
+ Read the current `plugins:` in `.rigor.dist.yml`, and the project's
41
+ dependencies in `Gemfile.lock`.
42
+
43
+ ### Phase 2 — match against the bundled catalogue
44
+
45
+ The authoritative, current list of bundled plugins (the count drifts as
46
+ new ones land) is the catalogue:
47
+
48
+ <https://github.com/rigortype/rigor/blob/master/plugins/README.md>
49
+
50
+ For each Gemfile.lock dependency, check whether a bundled `rigor-<gem>`
51
+ plugin exists and is **not** already in `plugins:`. Common matches:
52
+ `activerecord`/`actionpack` → the Rails plugins, `rspec` → `rigor-rspec`,
53
+ `dry-*` → the dry-rb plugins, `devise`, `sidekiq`, `sorbet`, etc. Add the
54
+ missing ones to `plugins:`.
55
+
56
+ ### Phase 3 — verify every plugin loads
57
+
58
+ ```sh
59
+ rigor plugins --strict
60
+ ```
61
+
62
+ `rigor plugins` reports the activation status of every configured plugin;
63
+ `--strict` exits non-zero on any activation failure (ideal as a CI gate).
64
+ Fix any failure it reports (a misspelled id, a missing `signature_paths:`)
65
+ before moving on.
66
+
67
+ ### Phase 4 — re-check
68
+
69
+ ```sh
70
+ rigor check
71
+ ```
72
+
73
+ The framework calls the newly-enabled plugins understand should now
74
+ resolve. If enabling a plugin surfaced new diagnostics, treat the diff as
75
+ usual (baseline regenerate in acknowledge mode, or fix genuine findings).
76
+
77
+ ## Next step
78
+
79
+ Re-run `rigor skill describe` for the next move.
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: rigor-protection-uplift
3
+ description: |
4
+ Close the type-protection holes `rigor coverage --protection` surfaces: for each unprotected dispatch site, run `rigor sig-gen` first, hand-author only the minimal residual annotation, then verify with a double gate — the site becomes protected AND `rigor check` gains no new diagnostic. Triggers: "raise type protection", "add types where Rigor can't catch bugs", "act on coverage --protection / --mutation output", "make more of this code bug-catchable". NOT for Rigor's own `lib/` or the bundled plugins (use `rigor sig-gen` directly there and treat gaps as engine signal), and NOT for first-time setup (use rigor-project-init).
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.1.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Rigor Protection Uplift
12
+
13
+ `rigor coverage --protection` (Tier 1) and `rigor coverage --protection
14
+ --mutation` (Tier 2) *surface* "add a type here" — they never author the
15
+ type. This skill *acts* on that surfacing under the discipline that keeps
16
+ Rigor false-positive-safe: protection goes up, and not one line of
17
+ working code starts reporting a new diagnostic.
18
+
19
+ ## When to use
20
+
21
+ - A user wants to raise how much of their code Rigor can actually catch
22
+ bugs in.
23
+ - You have `coverage --protection` output (an "add a type here" list) and
24
+ want to close it.
25
+
26
+ ## When NOT to use
27
+
28
+ - **Rigor's own `lib/`, or the bundled `plugins/` / `examples/`** — the
29
+ self-check tree. Hand-authoring types there collides with the
30
+ sig-gen-first ethos; run `rigor sig-gen` directly and treat residual
31
+ gaps as engine signal to report, not a private fix.
32
+ - **"Make my code more precise" with no protection goal** — that is
33
+ `rigor coverage` (precision), not `--protection`.
34
+ - **A project with no Rigor config yet** — onboard first with
35
+ `rigor-project-init`.
36
+
37
+ ## Load-bearing rules (read before touching a single type)
38
+
39
+ 1. **The signal prioritises and verifies; the contract sources the
40
+ type.** Never write the type the coverage/mutation signal "wants".
41
+ Write the type the code *actually has*, derived from the
42
+ implementation and its callers. A type guessed from the signal is a
43
+ false-confidence type — worse than no type.
44
+ 2. **sig-gen first.** A hand-written annotation is only the *residual*
45
+ `rigor sig-gen` cannot reach. Every residual is a sig-gen-improvement
46
+ to report, not just a private fix.
47
+ 3. **"Minimal" = annotation footprint, not minimal-to-kill-the-mutant.**
48
+ Optimising literally for mutant death games the metric. Add the
49
+ smallest *true* annotation that models the contract; if that also
50
+ kills the mutant, good.
51
+ 4. **Robustness (ADR-5).** Tighten returns, keep parameters lenient. An
52
+ over-tight param annotation breaks callers and breaches the
53
+ false-positive discipline.
54
+
55
+ ## Procedure
56
+
57
+ ### Phase 1 — surface the holes
58
+
59
+ ```sh
60
+ rigor coverage --protection --format json PATHS
61
+ ```
62
+
63
+ Read the ranked "add a type here" list (`count`, `method_name`,
64
+ `examples`). Optionally confirm the highest-traffic ones actually buy
65
+ catching power with the Tier 2 deep dive:
66
+
67
+ ```sh
68
+ rigor coverage --protection --mutation --format json # changed-files by default
69
+ ```
70
+
71
+ ### Phase 2 — sig-gen first
72
+
73
+ ```sh
74
+ rigor sig-gen --diff PATHS # inspect; --write to apply
75
+ ```
76
+
77
+ Adopt every concrete inferred signature. Note where sig-gen emits
78
+ `untyped` for a site on the "add a type here" list — that is the
79
+ **residual** Phase 3 owns.
80
+
81
+ ### Phase 3 — author the residual (cheapest carrier per hole class)
82
+
83
+ | Hole class | Cheapest carrier |
84
+ | ----------------------------------- | --------------------------------------------- |
85
+ | `Dynamic` method return | annotate that method's return in `sig/…rbs` |
86
+ | `Dynamic[top] \| nil` ivar read | `# @rbs @field: T` (ADR-58 territory) |
87
+ | untyped param feeding the receiver | a *lenient* param annotation |
88
+
89
+ Write the minimal true type. Prefer annotating the *upstream* source of
90
+ the Dynamic (the method return / the ivar) over the call site itself.
91
+
92
+ **Trap (carrier-additivity):** a sidecar `sig/…rbs` is NOT purely
93
+ additive. Declaring a class there flips it from inference-mode to
94
+ RBS-declared mode and *drops every member the RBS omits* — a lone
95
+ `def formatted: () -> String` can make Rigor forget the inferred
96
+ `initialize` and reject `Money.new(500)`. So either (1) adopt the full
97
+ Phase-2 sig-gen base into the file and add the residual on top, or
98
+ (2) use an in-place additive carrier (rbs-inline `#:` / a
99
+ `%a{rigor:v1:…}` return-override) that annotates the method without
100
+ re-declaring the class. "Minimal footprint" means the smallest *true*
101
+ type, never the smallest *file*.
102
+
103
+ ### Phase 4 — double-gate verify (both must hold)
104
+
105
+ ```sh
106
+ rigor coverage --protection PATHS # (a) the site is now protected / ratio up
107
+ rigor check PATHS # (b) no NEW diagnostic vs the post-sig-gen baseline
108
+ ```
109
+
110
+ If (b) regresses, the annotation modeled the wrong contract — **revert
111
+ it**, do not suppress the diagnostic. If (a) did not move, the carrier
112
+ was wrong (often: you typed the call site, not the upstream Dynamic
113
+ source). Gate (b) is a *diff* against the post-sig-gen state, not an
114
+ absolute "zero diagnostics" — sig-gen itself can surface the
115
+ acknowledge-mode FP envelope a project baseline absorbs; this skill owns
116
+ only the increment it adds.
117
+
118
+ ### Phase 5 — feed the residual back
119
+
120
+ File each Phase-3 residual as a sig-gen gap (what shape did inference
121
+ miss?). The hand annotation is the stopgap; the durable fix is raising
122
+ inference so the residual disappears.
123
+
124
+ ## Honest bounds
125
+
126
+ Hand-RBS uplift is a finisher, not a path to 80% protection. The
127
+ dominant remaining holes after this loop are intractable from
128
+ annotation alone — external-gem Dynamic receivers, polymorphic value
129
+ types, generic type parameters, dynamically-built classes. Those need
130
+ parametric types / external RBS / engine folding (and are where the
131
+ `rigor-plugin-author` escalation or a Rigor issue comes in), not another
132
+ hand annotation. Expect a low-20s → low-30s% protection lift on a mature
133
+ library, at zero diagnostic cost.
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: rigor-rbs-setup
3
+ description: |
4
+ Install community RBS for the project's gems with `rbs collection install`, so Rigor stops typing calls into RBS-less dependencies as `Dynamic` and gains real coverage (and real bug-catching) on them. Rigor auto-detects the resulting `rbs_collection.lock.yaml` — no Rigor config change needed. Triggers: "set up rbs collection", "my gems type as Dynamic", "rigor check says N gems have no RBS available", "reduce false positives from untyped gems". NOT for first-time Rigor setup (use rigor-project-init first) and NOT for a gem that has no entry in the community collection (that needs rigor-plugin-author or a Rigor issue).
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.1.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Rigor RBS Setup
12
+
13
+ Most of what Rigor cannot type on a real application is the surface of
14
+ third-party gems that ship **no RBS**: a call into such a gem returns
15
+ `Dynamic`, so every downstream method on the result is unprotected and
16
+ some genuine bugs go uncaught. The Ruby ecosystem's answer is the
17
+ community RBS collection at
18
+ [`ruby/gem_rbs_collection`](https://github.com/ruby/gem_rbs_collection);
19
+ this skill wires it into the project.
20
+
21
+ ## When to use
22
+
23
+ - `rigor check` ends with `info: N gem(s) in Gemfile.lock have no RBS
24
+ available: …` — that list is the gap this skill closes.
25
+ - A deep call chain into a gem (an HTTP client, a parser, a service
26
+ object) types as `Dynamic` and you want Rigor to see through it.
27
+
28
+ ## When NOT to use
29
+
30
+ - The project has no Rigor config yet — run `rigor-project-init` first.
31
+ - A specific gem you need is **not in** the community collection — RBS
32
+ setup will not cover it. That residue is for `rigor-plugin-author` (if
33
+ it is your own DSL) or a Rigor issue (if it is a popular gem worth
34
+ built-in support).
35
+
36
+ ## How Rigor consumes it (so you know what "done" looks like)
37
+
38
+ Rigor **auto-detects** `rbs_collection.lock.yaml` at the project root
39
+ (`rbs_collection.auto_detect` defaults to `true`) and feeds each gem's
40
+ downloaded RBS directory into its signature paths, skipping `stdlib`
41
+ entries it already bundles. **No `.rigor.yml` change is required** — the
42
+ lockfile's presence is the whole wiring. (Set `rbs_collection.lockfile:`
43
+ only if the lockfile lives somewhere other than the project root.)
44
+
45
+ ## Procedure
46
+
47
+ ### Phase 1 — see the gap
48
+
49
+ ```sh
50
+ rigor check
51
+ ```
52
+
53
+ Read the closing `info` line listing the gems with no RBS. That is your
54
+ target set.
55
+
56
+ ### Phase 2 — make the `rbs` CLI available
57
+
58
+ `rbs collection` is part of the `rbs` gem and reads the project's
59
+ `Gemfile.lock`. Check it is runnable:
60
+
61
+ ```sh
62
+ rbs --version # or: bundle exec rbs --version
63
+ ```
64
+
65
+ If it is missing, add it to the project's development group
66
+ (`bundle add rbs --group development`) or install it standalone
67
+ (`gem install rbs`). Prefer `bundle exec rbs …` when `rbs` is in the
68
+ Gemfile so it matches the project's resolved versions.
69
+
70
+ ### Phase 3 — initialise the collection config (first time only)
71
+
72
+ ```sh
73
+ rbs collection init
74
+ ```
75
+
76
+ This writes `rbs_collection.yaml` (which gems to pull, which to ignore).
77
+ Skip if the file already exists; review it either way — the default
78
+ pulls RBS for every gem in the lockfile that the collection knows.
79
+
80
+ ### Phase 4 — install
81
+
82
+ ```sh
83
+ rbs collection install # bundle exec rbs collection install when bundled
84
+ ```
85
+
86
+ This resolves against `ruby/gem_rbs_collection`, writes
87
+ `rbs_collection.lock.yaml`, and downloads the `.rbs` files into
88
+ `.gem_rbs_collection/`.
89
+
90
+ ### Phase 5 — verify (re-check; watch for new diagnostics)
91
+
92
+ ```sh
93
+ rigor check
94
+ ```
95
+
96
+ Two things to confirm:
97
+
98
+ 1. **The `info` "no RBS available" list shrank** — the gems now covered
99
+ dropped off it.
100
+ 2. **No surprising new diagnostics.** Community RBS occasionally ships a
101
+ signature stricter than the gem's real runtime behaviour, which can
102
+ surface a false positive. Treat the diff like any other:
103
+ - In **acknowledge mode**, regenerate the baseline
104
+ (`rigor baseline regenerate`) so the new envelope is recorded.
105
+ - For a **genuine** new error the sharper types caught, that is a win
106
+ — fix it.
107
+ - For a clear RBS-quality false positive, `# rigor:disable <rule>` the
108
+ site with a reason, or pin the gem out in `rbs_collection.yaml`.
109
+
110
+ ### Phase 6 — commit
111
+
112
+ Commit the **config + lockfile**, gitignore the **download dir**:
113
+
114
+ ```sh
115
+ # .gitignore
116
+ .gem_rbs_collection/
117
+ ```
118
+
119
+ Commit `rbs_collection.yaml` and `rbs_collection.lock.yaml` so every
120
+ contributor and CI resolves the same RBS. `.gem_rbs_collection/` is
121
+ regenerable from the lockfile (`rbs collection install`), so it is not
122
+ committed.
123
+
124
+ ## Next step
125
+
126
+ Re-run `rigor skill describe` — with the gem-RBS foundation in place it
127
+ will route you to the next move (CI wiring, baseline work, or protection
128
+ uplift).
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: rigor-upgrade
3
+ description: |
4
+ Adopt a new Rigor version cleanly: after upgrading the `rigortype` gem, re-run the analysis, diff the diagnostics against the committed baseline, and sort the changes into genuine new catches (sharper inference), known sig-quality false positives, and the baseline you should regenerate. Triggers: "I upgraded Rigor, what changed?", "new diagnostics after gem update rigortype", "adopt the new Rigor version", "rigor baseline drifted after upgrade". NOT for first-time setup (use rigor-project-init) or routine baseline work unrelated to an upgrade (use rigor-baseline-reduce).
5
+ license: MPL-2.0
6
+ metadata:
7
+ version: 0.1.0
8
+ homepage: https://github.com/rigortype/rigor
9
+ ---
10
+
11
+ # Rigor Upgrade
12
+
13
+ A new Rigor release sharpens inference and may add or tighten rules, so
14
+ `rigor check` can report diagnostics it did not before — most are the new
15
+ version catching more, a few are sig-quality false positives the new
16
+ sharpness exposes. This skill adopts the upgrade without either blindly
17
+ regenerating the baseline (which buries genuine new catches) or treating
18
+ every new line as a regression.
19
+
20
+ ## When to use
21
+
22
+ - You just ran `mise use gem:rigortype` / `gem update rigortype` and want
23
+ to understand what the new version changed about your project's
24
+ diagnostics.
25
+
26
+ ## Procedure
27
+
28
+ ### Phase 1 — confirm the new version
29
+
30
+ ```sh
31
+ rigor --version
32
+ ```
33
+
34
+ ### Phase 2 — see the delta against the committed baseline
35
+
36
+ ```sh
37
+ rigor check
38
+ rigor diff # compare current diagnostics to the saved baseline JSON
39
+ ```
40
+
41
+ `rigor diff` shows what is **new** relative to the baseline — that set is
42
+ what the upgrade changed.
43
+
44
+ ### Phase 3 — sort the new diagnostics
45
+
46
+ For each newly-surfaced diagnostic:
47
+
48
+ - **A genuine new catch** — the sharper inference found a real latent
49
+ issue. Fix it. (Check `evidence_tier` in `rigor check --format json`:
50
+ `high` is most likely a true positive.)
51
+ - **A sig-quality false positive** — a known class (Struct
52
+ `call.wrong-arity`, an over-nilable RBS return, a regex-capture `$1`
53
+ read). `# rigor:disable <rule>` the site with a reason, or address the
54
+ RBS. Read `rigor explain <rule>` if unsure whether the rule should fire
55
+ here.
56
+ - **Expected envelope growth** — broadly acceptable in acknowledge mode.
57
+
58
+ ### Phase 4 — regenerate the baseline (acknowledge mode)
59
+
60
+ Once you have triaged and fixed the genuine catches, record the new
61
+ envelope so the regeneration does not bury what you just fixed:
62
+
63
+ ```sh
64
+ rigor baseline regenerate
65
+ ```
66
+
67
+ Commit the updated `.rigor-baseline.yml` together with any fixes, so the
68
+ team adopts the same post-upgrade baseline.
69
+
70
+ ## Note
71
+
72
+ `rigor skill describe` cannot detect that you just upgraded — the
73
+ baseline records only its schema version, not the Rigor version that
74
+ generated it — so this skill is invoked on demand rather than
75
+ recommended automatically. Run it whenever you bump the gem.
76
+
77
+ ## Next step
78
+
79
+ Re-run `rigor skill describe` for the next move.