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.
- checksums.yaml +4 -4
- data/README.md +41 -14
- data/docs/handbook/01-getting-started.md +311 -0
- data/docs/handbook/02-everyday-types.md +337 -0
- data/docs/handbook/03-narrowing.md +359 -0
- data/docs/handbook/04-tuples-and-shapes.md +321 -0
- data/docs/handbook/05-methods-and-blocks.md +339 -0
- data/docs/handbook/06-classes.md +305 -0
- data/docs/handbook/07-rbs-and-extended.md +427 -0
- data/docs/handbook/08-understanding-errors.md +373 -0
- data/docs/handbook/09-plugins.md +241 -0
- data/docs/handbook/10-sorbet.md +347 -0
- data/docs/handbook/11-sig-gen.md +312 -0
- data/docs/handbook/12-lightweight-hkt.md +333 -0
- data/docs/handbook/README.md +275 -0
- data/docs/handbook/appendix-elixir.md +370 -0
- data/docs/handbook/appendix-go.md +399 -0
- data/docs/handbook/appendix-java-csharp.md +470 -0
- data/docs/handbook/appendix-liskov.md +580 -0
- data/docs/handbook/appendix-mypy.md +370 -0
- data/docs/handbook/appendix-phpstan.md +338 -0
- data/docs/handbook/appendix-protocols-and-structural-typing.md +292 -0
- data/docs/handbook/appendix-rust.md +446 -0
- data/docs/handbook/appendix-steep.md +336 -0
- data/docs/handbook/appendix-type-theory.md +1662 -0
- data/docs/handbook/appendix-typeprof.md +416 -0
- data/docs/handbook/appendix-typescript.md +332 -0
- data/docs/install.md +189 -0
- data/docs/llms.txt +72 -0
- data/docs/manual/01-installation.md +342 -0
- data/docs/manual/02-cli-reference.md +569 -0
- data/docs/manual/03-configuration.md +152 -0
- data/docs/manual/04-diagnostics.md +206 -0
- data/docs/manual/05-inspecting-types.md +109 -0
- data/docs/manual/06-baseline.md +104 -0
- data/docs/manual/07-plugins.md +92 -0
- data/docs/manual/08-skills.md +143 -0
- data/docs/manual/09-editor-integration.md +245 -0
- data/docs/manual/10-mcp-server.md +539 -0
- data/docs/manual/11-ci.md +274 -0
- data/docs/manual/12-caching.md +116 -0
- data/docs/manual/13-troubleshooting.md +120 -0
- data/docs/manual/14-rails-quickstart.md +332 -0
- data/docs/manual/15-type-protection-coverage.md +204 -0
- data/docs/manual/16-rbs-extended-annotations.md +190 -0
- data/docs/manual/17-driving-improvement.md +160 -0
- data/docs/manual/README.md +87 -0
- data/docs/manual/ci-templates/README.md +58 -0
- data/docs/manual/plugins/README.md +86 -0
- data/docs/manual/plugins/rigor-actioncable.md +78 -0
- data/docs/manual/plugins/rigor-actionmailer.md +74 -0
- data/docs/manual/plugins/rigor-actionpack.md +80 -0
- data/docs/manual/plugins/rigor-activejob.md +58 -0
- data/docs/manual/plugins/rigor-activerecord.md +102 -0
- data/docs/manual/plugins/rigor-activestorage.md +74 -0
- data/docs/manual/plugins/rigor-activesupport-core-ext.md +86 -0
- data/docs/manual/plugins/rigor-devise.md +70 -0
- data/docs/manual/plugins/rigor-dry-schema.md +56 -0
- data/docs/manual/plugins/rigor-dry-struct.md +60 -0
- data/docs/manual/plugins/rigor-dry-types.md +59 -0
- data/docs/manual/plugins/rigor-dry-validation.md +62 -0
- data/docs/manual/plugins/rigor-factorybot.md +76 -0
- data/docs/manual/plugins/rigor-graphql.md +89 -0
- data/docs/manual/plugins/rigor-hanami.md +83 -0
- data/docs/manual/plugins/rigor-mangrove.md +73 -0
- data/docs/manual/plugins/rigor-minitest.md +86 -0
- data/docs/manual/plugins/rigor-pundit.md +72 -0
- data/docs/manual/plugins/rigor-rails-i18n.md +92 -0
- data/docs/manual/plugins/rigor-rails-routes.md +94 -0
- data/docs/manual/plugins/rigor-rails.md +44 -0
- data/docs/manual/plugins/rigor-rbs-inline.md +83 -0
- data/docs/manual/plugins/rigor-rspec-rails.md +72 -0
- data/docs/manual/plugins/rigor-rspec.md +86 -0
- data/docs/manual/plugins/rigor-shoulda-matchers.md +78 -0
- data/docs/manual/plugins/rigor-sidekiq.md +78 -0
- data/docs/manual/plugins/rigor-sinatra.md +61 -0
- data/docs/manual/plugins/rigor-sorbet.md +63 -0
- data/docs/manual/plugins/rigor-statesman.md +75 -0
- data/docs/manual/plugins/rigor-typescript-utility-types.md +71 -0
- data/exe/rigor +1 -1
- data/lib/rigor/analysis/incremental_session.rb +4 -2
- data/lib/rigor/analysis/run_stats.rb +13 -1
- data/lib/rigor/analysis/runner.rb +54 -12
- data/lib/rigor/cli/check_command.rb +1 -1
- data/lib/rigor/cli/docs_command.rb +248 -0
- data/lib/rigor/cli/skill_command.rb +103 -41
- data/lib/rigor/cli/skill_describe.rb +346 -0
- data/lib/rigor/cli/triage_command.rb +8 -2
- data/lib/rigor/cli/triage_renderer.rb +4 -0
- data/lib/rigor/cli.rb +25 -3
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +124 -32
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +37 -6
- data/lib/rigor/inference/scope_indexer.rb +87 -89
- data/lib/rigor/plugin/isolation.rb +5 -5
- data/lib/rigor/plugin/loader.rb +4 -2
- data/lib/rigor/triage/catalogue.rb +16 -1
- data/lib/rigor/triage.rb +30 -7
- data/lib/rigor/version.rb +1 -1
- data/skills/rigor-ask/SKILL.md +172 -0
- data/skills/rigor-doctor/SKILL.md +87 -0
- data/skills/rigor-editor-setup/SKILL.md +114 -0
- data/skills/rigor-mcp-setup/SKILL.md +117 -0
- data/skills/rigor-monkeypatch-resolve/SKILL.md +79 -0
- data/skills/rigor-next-steps/SKILL.md +113 -0
- data/skills/rigor-plugin-tune/SKILL.md +79 -0
- data/skills/rigor-protection-uplift/SKILL.md +133 -0
- data/skills/rigor-rbs-setup/SKILL.md +128 -0
- data/skills/rigor-upgrade/SKILL.md +79 -0
- 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.
|