rigortype 0.2.5 → 0.2.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 +4 -3
- data/docs/handbook/09-plugins.md +5 -2
- data/docs/handbook/appendix-liskov.md +5 -3
- data/docs/handbook/appendix-phpstan.md +2 -2
- data/docs/install.md +1 -1
- data/docs/manual/02-cli-reference.md +60 -2
- data/docs/manual/06-baseline.md +12 -0
- data/docs/manual/08-skills.md +21 -0
- data/docs/manual/11-ci.md +6 -6
- data/docs/manual/15-type-protection-coverage.md +29 -0
- data/docs/manual/plugins/rigor-minitest.md +1 -1
- data/lib/rigor/cli/check_command.rb +4 -33
- data/lib/rigor/cli/check_runner_factory.rb +63 -0
- data/lib/rigor/cli/coverage_command.rb +42 -10
- data/lib/rigor/cli/doctor_command.rb +295 -0
- data/lib/rigor/cli/plugins_command.rb +2 -2
- data/lib/rigor/cli/plugins_renderer.rb +1 -1
- data/lib/rigor/cli/protection_renderer.rb +32 -2
- data/lib/rigor/cli/protection_report.rb +32 -6
- data/lib/rigor/cli/skill_command.rb +52 -1
- data/lib/rigor/cli/upgrade_command.rb +25 -0
- data/lib/rigor/cli.rb +17 -1
- data/lib/rigor/environment/rbs_loader.rb +28 -0
- data/lib/rigor/flow_contribution/fact.rb +1 -1
- data/lib/rigor/inference/dynamic_origin.rb +67 -0
- data/lib/rigor/inference/expression_typer.rb +22 -10
- data/lib/rigor/inference/fallback.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +16 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +41 -2
- data/lib/rigor/inference/method_dispatcher.rb +19 -4
- data/lib/rigor/inference/mutation_widening.rb +18 -0
- data/lib/rigor/inference/protection_scanner.rb +6 -3
- data/lib/rigor/inference/statement_evaluator.rb +5 -8
- data/lib/rigor/plugin/base.rb +34 -7
- data/lib/rigor/plugin/registry.rb +1 -1
- data/lib/rigor/scope.rb +16 -5
- data/lib/rigor/sig_gen/generator.rb +25 -0
- data/lib/rigor/sig_gen/method_candidate.rb +7 -2
- data/lib/rigor/sig_gen/writer.rb +60 -13
- data/lib/rigor/version.rb +1 -1
- data/lib/rigor.rb +1 -0
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +63 -2
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +2 -3
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +14 -24
- data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +10 -3
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +1 -1
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +36 -79
- data/sig/rigor/plugin/base.rbs +2 -0
- data/sig/rigor/scope.rbs +3 -1
- data/skills/rigor-ask/SKILL.md +21 -1
- data/skills/rigor-baseline-reduce/SKILL.md +16 -0
- data/skills/rigor-ci-setup/SKILL.md +96 -249
- data/skills/rigor-doctor/SKILL.md +39 -49
- data/skills/rigor-doctor/references/01-checks.md +52 -0
- data/skills/rigor-editor-setup/SKILL.md +14 -0
- data/skills/rigor-mcp-setup/SKILL.md +14 -0
- data/skills/rigor-monkeypatch-resolve/SKILL.md +15 -0
- data/skills/rigor-plugin-author/SKILL.md +24 -5
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +8 -4
- data/skills/rigor-plugin-review/SKILL.md +174 -0
- data/skills/rigor-plugin-review/references/01-best-practices-checklist.md +214 -0
- data/skills/rigor-plugin-tune/SKILL.md +21 -2
- data/skills/rigor-project-init/SKILL.md +16 -0
- data/skills/rigor-protection-uplift/SKILL.md +15 -0
- data/skills/rigor-rbs-setup/SKILL.md +15 -0
- data/skills/rigor-upgrade/SKILL.md +16 -0
- metadata +11 -4
|
@@ -29,6 +29,20 @@ This skill is the *workflow* around it (identify the editor → apply the
|
|
|
29
29
|
manual's snippet → verify), so it does not duplicate (and cannot
|
|
30
30
|
stale-out) the config details.
|
|
31
31
|
|
|
32
|
+
## First: load the version-current copy
|
|
33
|
+
|
|
34
|
+
The config details already come live from `rigor docs`; this section keeps
|
|
35
|
+
the *workflow itself* current too. Prefer the copy of this skill that ships
|
|
36
|
+
with the **installed** Rigor over any vendored or frozen copy of this file:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
rigor skill --full rigor-editor-setup
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
43
|
+
copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
|
|
44
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
45
|
+
|
|
32
46
|
## When to use
|
|
33
47
|
|
|
34
48
|
- A developer wants Rigor feedback live in their editor, not just from
|
|
@@ -31,6 +31,20 @@ This skill is the *workflow* around it (identify the client → apply the
|
|
|
31
31
|
manual's snippet → verify the handshake), so it does not duplicate (and
|
|
32
32
|
cannot stale-out) the config details.
|
|
33
33
|
|
|
34
|
+
## First: load the version-current copy
|
|
35
|
+
|
|
36
|
+
The config details already come live from `rigor docs`; this keeps the
|
|
37
|
+
*workflow itself* current too. Prefer the copy of this skill that ships
|
|
38
|
+
with the **installed** Rigor over any vendored or frozen copy of this file:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
rigor skill --full rigor-mcp-setup
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
45
|
+
copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
|
|
46
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
47
|
+
|
|
34
48
|
## When to use
|
|
35
49
|
|
|
36
50
|
- A developer wants their AI agent to call Rigor's tools mid-session.
|
|
@@ -18,6 +18,21 @@ top-level helper). `pre_eval:` ([ADR-17](https://github.com/rigortype/rigor/blob
|
|
|
18
18
|
fixes this: it pre-evaluates the listed project files and registers every
|
|
19
19
|
method they define with a **literal** `def` / `def self.`.
|
|
20
20
|
|
|
21
|
+
## First: load the version-current copy
|
|
22
|
+
|
|
23
|
+
This skill's exact commands, flags, and config keys drift between Rigor
|
|
24
|
+
releases, so follow the copy that ships with the **installed** Rigor rather
|
|
25
|
+
than any vendored or frozen copy of this file. Get the complete current
|
|
26
|
+
procedure in one call:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
rigor skill --full rigor-monkeypatch-resolve
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
33
|
+
copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
|
|
34
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
35
|
+
|
|
21
36
|
## When to use
|
|
22
37
|
|
|
23
38
|
- `rigor triage` shows a cluster of `call.undefined-method` /
|
|
@@ -27,6 +27,22 @@ environment.
|
|
|
27
27
|
> covers the in-repo layout, `plugin_helpers.rb`, and `make verify`.
|
|
28
28
|
> This skill is for plugins that live in *your* project.
|
|
29
29
|
|
|
30
|
+
## First: load the version-current copy
|
|
31
|
+
|
|
32
|
+
The plugin contract is pre-1.0 and moving (see the next section), so this
|
|
33
|
+
skill's step detail — in its `references/` files — is exactly the kind that
|
|
34
|
+
drifts between releases. Follow the copy that ships with the **installed**
|
|
35
|
+
Rigor rather than any vendored or frozen copy of this file. Get the complete
|
|
36
|
+
current procedure (body + all references, inline) in one call:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
rigor skill --full rigor-plugin-author
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
43
|
+
copy — just proceed. If the `rigor` command is not available, run
|
|
44
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
45
|
+
|
|
30
46
|
## Important — the plugin contract is a preview (pre-1.0)
|
|
31
47
|
|
|
32
48
|
Rigor's plugin contract (ADR-2) is **not yet frozen**. It stabilises
|
|
@@ -97,19 +113,22 @@ AST walk per file — hands every matching node to the block along with a
|
|
|
97
113
|
`scope` it can query for inferred types. The block returns an array of
|
|
98
114
|
`Rigor::Analysis::Diagnostic` (built via the `diagnostic` helper).
|
|
99
115
|
Optionally the plugin also declares `dynamic_return(receivers:)` /
|
|
100
|
-
`
|
|
101
|
-
for call sites the core analyzer types as `Dynamic`.
|
|
116
|
+
`narrowing_facts(methods:)` to *supply* a return type or narrowing facts
|
|
117
|
+
for call sites the core analyzer types as `Dynamic`. (`narrowing_facts`
|
|
118
|
+
was renamed from `type_specifier` in ADR-80; `type_specifier` remains as a
|
|
119
|
+
deprecating alias removed in 0.3.0 — use `narrowing_facts` in new plugins.)
|
|
120
|
+
`#diagnostics_for_file`
|
|
102
121
|
is the file-rule surface for whole-file diagnostics a per-node walk can't
|
|
103
122
|
express. (`flow_contribution_for` was removed pre-1.0 in ADR-52 WD3 —
|
|
104
123
|
defining it now raises `ArgumentError`; use `dynamic_return` /
|
|
105
|
-
`
|
|
124
|
+
`narrowing_facts`. See Phase 2.)
|
|
106
125
|
|
|
107
126
|
## Phase outline
|
|
108
127
|
|
|
109
128
|
| Phase | What | Reference |
|
|
110
129
|
| --- | --- | --- |
|
|
111
130
|
| 1 | Package and scaffold — gem vs project-private layout, gemspec / Gemfile, the plugin class skeleton, `.rigor.yml` activation. | [`references/01-plan-and-scaffold.md`](references/01-plan-and-scaffold.md) |
|
|
112
|
-
| 2 | Node rules — `node_rule` (engine-owned walk), building `Diagnostic`s via `Base#diagnostic`, querying `scope.type_of`, calling the target library directly instead of reimplementing it (ADR-39: `Plugin::Inflector`, `Base.suggest`), optional `dynamic_return` / `
|
|
131
|
+
| 2 | Node rules — `node_rule` (engine-owned walk), building `Diagnostic`s via `Base#diagnostic`, querying `scope.type_of`, calling the target library directly instead of reimplementing it (ADR-39: `Plugin::Inflector`, `Base.suggest`), optional `dynamic_return` / `narrowing_facts`, RBS for the DSL. | [`references/02-walker-and-types.md`](references/02-walker-and-types.md) |
|
|
113
132
|
| 3 | Test and ship — fixture-based tests (RSpec / Minitest, no rigor internals), version pinning, README, publish or keep private. | [`references/03-test-and-ship.md`](references/03-test-and-ship.md) |
|
|
114
133
|
|
|
115
134
|
## Reading order — modules
|
|
@@ -117,5 +136,5 @@ defining it now raises `ArgumentError`; use `dynamic_return` /
|
|
|
117
136
|
| Module | Read | Covers |
|
|
118
137
|
| --- | --- | --- |
|
|
119
138
|
| 1 | [`references/01-plan-and-scaffold.md`](references/01-plan-and-scaffold.md) | **Phase 1.** The gem vs project-private packaging split, directory trees for both, gemspec template, project-private path-gem / `RUBYLIB` activation, the `Rigor::Plugin::Base` skeleton, `.rigor.yml` `plugins:` wiring. |
|
|
120
|
-
| 2 | [`references/02-walker-and-types.md`](references/02-walker-and-types.md) | **Phase 2.** The `node_rule` engine-owned AST walk over Prism nodes, the `Base#diagnostic` helper, asking the analyzer for inferred types via `scope.type_of`, two-pass / lexical context (`node_file_context` / `NodeContext`), the optional `dynamic_return` / `
|
|
139
|
+
| 2 | [`references/02-walker-and-types.md`](references/02-walker-and-types.md) | **Phase 2.** The `node_rule` engine-owned AST walk over Prism nodes, the `Base#diagnostic` helper, asking the analyzer for inferred types via `scope.type_of`, two-pass / lexical context (`node_file_context` / `NodeContext`), the optional `dynamic_return` / `narrowing_facts` return-type hooks (`narrowing_facts` was renamed from `type_specifier` in ADR-80, alias removed in 0.3.0; `flow_contribution_for` was removed pre-1.0 in ADR-52 WD3), calling the target library's pure methods directly rather than reimplementing them (ADR-39: `Plugin::Inflector` over the real `ActiveSupport::Inflector`; `Base.suggest` for did-you-mean), and shipping `sig/*.rbs` so the DSL's types are visible. |
|
|
121
140
|
| 3 | [`references/03-test-and-ship.md`](references/03-test-and-ship.md) | **Phase 3.** Testing a plugin from outside the monorepo — fixture projects driven through `rigor check --format json`, plus pure unit tests of dispatch tables — with RSpec or Minitest. Version pinning against the pre-1.0 contract. README. Publishing to RubyGems or keeping the plugin private. |
|
|
@@ -162,7 +162,7 @@ rather than hand-rolling Levenshtein:
|
|
|
162
162
|
Rigor::Plugin::Base.suggest(typo, known_names) # nearest match, or nil
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
## Optional — contribute a return type with `dynamic_return` / `
|
|
165
|
+
## Optional — contribute a return type with `dynamic_return` / `narrowing_facts`
|
|
166
166
|
|
|
167
167
|
> **Critical — these hooks do NOT make a method "defined", so they do
|
|
168
168
|
> NOT suppress `call.undefined-method`.** Method *existence* and call
|
|
@@ -197,11 +197,15 @@ end
|
|
|
197
197
|
# Post-return NARROWING FACTS, gated on the call's method name.
|
|
198
198
|
# Return an Array of facts (or nil). Used for assertion / predicate
|
|
199
199
|
# narrowing (`assert_kind_of(Foo, x)` ⇒ x is Foo afterwards).
|
|
200
|
-
|
|
200
|
+
narrowing_facts methods: [:assert_kind_of] do |call_node, scope|
|
|
201
201
|
# ... build and return the post-return facts ...
|
|
202
202
|
end
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
+
> **`narrowing_facts` was renamed from `type_specifier` in ADR-80.**
|
|
206
|
+
> `type_specifier` remains as a deprecating alias, removed in 0.3.0 — use
|
|
207
|
+
> `narrowing_facts` in new plugins.
|
|
208
|
+
|
|
205
209
|
Build return types with `Rigor::Type::Combinator`:
|
|
206
210
|
|
|
207
211
|
```ruby
|
|
@@ -240,7 +244,7 @@ If the DSL introduces methods or classes that Rigor cannot see (a
|
|
|
240
244
|
Rigor RBS declaring them so *core* inference — not just your plugin —
|
|
241
245
|
treats them as **defined**. This is what removes the
|
|
242
246
|
`call.undefined-method` diagnostics on those methods; nothing else
|
|
243
|
-
(not a `node_rule`, not `dynamic_return` / `
|
|
247
|
+
(not a `node_rule`, not `dynamic_return` / `narrowing_facts`) makes a
|
|
244
248
|
method exist in Rigor's view.
|
|
245
249
|
|
|
246
250
|
Two ways to wire the RBS, depending on how the plugin is packaged:
|
|
@@ -288,6 +292,6 @@ They compose — many plugins ship both.
|
|
|
288
292
|
|
|
289
293
|
A plugin whose `node_rule`(s) recognise the DSL and emit diagnostics
|
|
290
294
|
with correct severities and rule ids — optionally a `dynamic_return` /
|
|
291
|
-
`
|
|
295
|
+
`narrowing_facts` and a `sig/` bundle. Verify by eye with `rigor check`;
|
|
292
296
|
lock it down with tests in Phase 3
|
|
293
297
|
([`03-test-and-ship.md`](03-test-and-ship.md)).
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rigor-plugin-review
|
|
3
|
+
description: |
|
|
4
|
+
Review an existing Rigor plugin's source against the current authoring contract and produce a prioritized upgrade path — the modernization counterpart to rigor-plugin-author. Audits config-default declaration (ADR-40), the AST-walk model (node_rule vs a hand-rolled traversal), return-type / narrowing hooks (dynamic_return / narrowing_facts, not the removed flow_contribution_for or deprecated type_specifier), the ADR-60 WD4 authoring helpers (diagnostic / diagnostics_for / suggest / producer_value / read_fact), engine-collaboration vs reimplementation, cache-producer soundness, manifest-field hygiene, and doc freshness. Triggers: "review this Rigor plugin", "does my plugin follow best practices", "upgrade our rigor-prefixed plugin to the latest contract", "modernize this plugin", "is this plugin using the current API". NOT for authoring a new plugin (use rigor-plugin-author), enabling bundled plugins on a project (use rigor-plugin-tune), or tuning plugin config.
|
|
5
|
+
license: MPL-2.0
|
|
6
|
+
metadata:
|
|
7
|
+
version: 0.1.0
|
|
8
|
+
homepage: https://github.com/rigortype/rigor
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Rigor Plugin Review
|
|
12
|
+
|
|
13
|
+
Audit an **existing** Rigor plugin — a bundled one in the rigor
|
|
14
|
+
monorepo (`plugins/` or `examples/`), or your own `rigor-<id>` gem —
|
|
15
|
+
against the current `Rigor::Plugin::Base` authoring contract, and hand
|
|
16
|
+
back a **prioritized upgrade path**. This is the review / upgrade
|
|
17
|
+
counterpart to `rigor-plugin-author` (which creates new plugins).
|
|
18
|
+
|
|
19
|
+
Plugins written before a contract addition keep working — the gate is
|
|
20
|
+
compatibility, not currency — but they drift from the idiom other
|
|
21
|
+
authors copy. The commonest drift, in rough order of how often it
|
|
22
|
+
appears:
|
|
23
|
+
|
|
24
|
+
1. **Config defaults** declared with a `DEFAULT_*` constant +
|
|
25
|
+
`config.fetch(k, DEFAULT)` instead of `config_schema {kind:,
|
|
26
|
+
default:}` (ADR-40).
|
|
27
|
+
2. **Hand-rolled boilerplate** the ADR-60 WD4 authoring helpers now
|
|
28
|
+
own — a Levenshtein "did you mean", a `@table`/`@load_error` memo +
|
|
29
|
+
rescue, a `Diagnostic.new` where a node exists.
|
|
30
|
+
3. **A hand-rolled AST walk** where the engine-owned `node_rule` now
|
|
31
|
+
fits (ADR-37 / ADR-52).
|
|
32
|
+
4. **Removed / renamed hooks** still named — `flow_contribution_for`
|
|
33
|
+
(deleted, ADR-52 WD3) or `type_specifier` (deprecated alias for
|
|
34
|
+
`narrowing_facts`, ADR-80).
|
|
35
|
+
5. **Stale docs** — archaeology about deleted hooks, pinned version
|
|
36
|
+
references that no longer mean anything.
|
|
37
|
+
|
|
38
|
+
## First: load the version-current copy
|
|
39
|
+
|
|
40
|
+
This skill audits against a contract that moves release to release (hook
|
|
41
|
+
renames, new helpers, deprecations), so its checklist — in its
|
|
42
|
+
`references/` files — is only as good as the Rigor it ships with. Follow
|
|
43
|
+
the copy that ships with the **installed** Rigor rather than any vendored
|
|
44
|
+
or frozen copy of this file. Get the complete current procedure (body + all
|
|
45
|
+
references, inline) in one call:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
rigor skill --full rigor-plugin-review
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
52
|
+
copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
|
|
53
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
54
|
+
|
|
55
|
+
## When to use / not use
|
|
56
|
+
|
|
57
|
+
**Use it** when someone asks to review a plugin's quality, check it
|
|
58
|
+
against best practices, or upgrade it to the current contract — whether
|
|
59
|
+
it lives in the rigor monorepo or in an external repo.
|
|
60
|
+
|
|
61
|
+
**Do not use it** for:
|
|
62
|
+
|
|
63
|
+
- **Authoring a new plugin** → `rigor-plugin-author`.
|
|
64
|
+
- **Choosing / enabling bundled plugins on a project** →
|
|
65
|
+
`rigor-plugin-tune`.
|
|
66
|
+
- **A behavioural bug in a plugin** — that is ordinary debugging, not a
|
|
67
|
+
contract-conformance pass.
|
|
68
|
+
|
|
69
|
+
## Read the plugin — and read the contract
|
|
70
|
+
|
|
71
|
+
Rigor is installed on disk, so both the plugin under review and the
|
|
72
|
+
worked-example plugins are readable source:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
rigor plugin list # every bundled + example plugin, with paths
|
|
76
|
+
rigor plugin print rigor-<id> # a plugin's main source, inline
|
|
77
|
+
rigor plugin path rigor-<id> # its directory, to browse
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The **authoritative** authoring surface — the one this review scores
|
|
81
|
+
against — is the internal spec, not this file:
|
|
82
|
+
|
|
83
|
+
- [`docs/internal-spec/plugin.md`](https://github.com/rigortype/rigor/blob/master/docs/internal-spec/plugin.md)
|
|
84
|
+
— manifest, `node_rule` / `node_file_context`, `dynamic_return` /
|
|
85
|
+
`narrowing_facts`, the `#diagnostic` / `#diagnostics_for` / `.suggest`
|
|
86
|
+
/ `#read_fact` author helpers, `config_schema` `{kind:, default:}`.
|
|
87
|
+
- [`docs/internal-spec/plugin-cache-producers.md`](https://github.com/rigortype/rigor/blob/master/docs/internal-spec/plugin-cache-producers.md)
|
|
88
|
+
— `producer` / `#cache_for` / `#producer_value` / `#producer_error`,
|
|
89
|
+
ADR-60 WD3 record-and-validate.
|
|
90
|
+
- [`docs/internal-spec/plugin-trust.md`](https://github.com/rigortype/rigor/blob/master/docs/internal-spec/plugin-trust.md)
|
|
91
|
+
— `TrustPolicy` / `IoBoundary`.
|
|
92
|
+
|
|
93
|
+
When the checklist below and the spec disagree, **the spec binds** —
|
|
94
|
+
it tracks the installed `rigor` version; this skill is a snapshot.
|
|
95
|
+
|
|
96
|
+
## Procedure
|
|
97
|
+
|
|
98
|
+
### Phase 1 — Inventory
|
|
99
|
+
|
|
100
|
+
Read the plugin's `lib/**/*.rb`, its `README.md`, and its integration /
|
|
101
|
+
unit spec. Note: the `manifest(...)` block, every `config.fetch` /
|
|
102
|
+
`DEFAULT_*` constant, every `Rigor::Analysis::Diagnostic.new`, any
|
|
103
|
+
hand-rolled `levenshtein` / `each_child` walk / cross-plugin `@*_resolved`
|
|
104
|
+
flag, and every mention of `flow_contribution_for` / `type_specifier`.
|
|
105
|
+
|
|
106
|
+
### Phase 2 — Score against the checklist
|
|
107
|
+
|
|
108
|
+
Walk [`references/01-best-practices-checklist.md`](references/01-best-practices-checklist.md)
|
|
109
|
+
concern by concern. For each finding, record: the smell, the modern
|
|
110
|
+
replacement, the authoritative citation, and — critically — whether the
|
|
111
|
+
change is **mechanical** (byte-identical diagnostics expected) or
|
|
112
|
+
**design-level** (needs judgment / may change behaviour).
|
|
113
|
+
|
|
114
|
+
### Phase 3 — Establish the oracle BEFORE changing anything
|
|
115
|
+
|
|
116
|
+
The plugin's integration spec is the contract you must preserve. Run it
|
|
117
|
+
green first, so you can prove each later step is a faithful refactor:
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
# external gem:
|
|
121
|
+
bundle exec rspec spec/
|
|
122
|
+
# in the rigor monorepo:
|
|
123
|
+
nix … develop --command bundle exec rspec spec/integration/<plugins|examples>/<id>_plugin_spec.rb
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If there is no spec, **write one first** (per `rigor-plugin-author`
|
|
127
|
+
Phase 3) — a modernization with no oracle is a guess.
|
|
128
|
+
|
|
129
|
+
### Phase 4 — Apply the upgrade path, mechanical first
|
|
130
|
+
|
|
131
|
+
Order the work low-risk → high-risk, and **re-run the spec after every
|
|
132
|
+
step**:
|
|
133
|
+
|
|
134
|
+
1. **Mechanical (expect byte-identical diagnostics):** ADR-40 config
|
|
135
|
+
defaults · helper swaps (`suggest` / `producer_value` / `diagnostic`
|
|
136
|
+
/ `diagnostics_for` / `read_fact`) · manifest-field renames (ADR-60
|
|
137
|
+
WD1/WD2) · doc freshness. A spec that changes here means the swap was
|
|
138
|
+
not faithful — fix it, do not re-baseline.
|
|
139
|
+
2. **Design-level (may change behaviour — validate empirically):** an
|
|
140
|
+
AST-walk migration onto `node_rule`; an engine-collaboration refactor
|
|
141
|
+
that reads `Scope#type_of` instead of a hand-rolled binding map.
|
|
142
|
+
**Do not delete hand-rolled state before proving the engine gives you
|
|
143
|
+
the same information** — see the `rigor-units` trap in the checklist
|
|
144
|
+
(the diagnostics-side `Scope` is a *seed entry scope* without
|
|
145
|
+
flow-accumulated local bindings, so a cross-statement binding map can
|
|
146
|
+
be necessary, not redundant).
|
|
147
|
+
|
|
148
|
+
### Phase 5 — Verify
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
rigor check <plugin>/lib # ADR-43 contract self-check — MUST be clean
|
|
152
|
+
rigor plugins --strict # the plugin still loads
|
|
153
|
+
rigor plugins --capabilities # node-rule types / dynamic_return receivers look right
|
|
154
|
+
bundle exec rspec … # the oracle spec, still green
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
In the **rigor monorepo**, the gate is `make check-plugins` (runs
|
|
158
|
+
`rigor check` over every `plugins/*/lib` + `examples/*/lib`) plus
|
|
159
|
+
`make verify`; land the change as its own commit(s) with the spec as
|
|
160
|
+
the byte-identical gate.
|
|
161
|
+
|
|
162
|
+
## Output
|
|
163
|
+
|
|
164
|
+
Hand the user a table — smell → replacement → authority → mechanical /
|
|
165
|
+
design — ranked so the mechanical, oracle-gated wins land first, and
|
|
166
|
+
call out any finding (like the units binding-map case) where the
|
|
167
|
+
"obvious" modernization is actually wrong. If nothing is stale, say so
|
|
168
|
+
plainly: a plugin that already tracks the current contract is a pass,
|
|
169
|
+
not an occasion to invent churn.
|
|
170
|
+
|
|
171
|
+
## Next step
|
|
172
|
+
|
|
173
|
+
Re-run `rigor skill describe` for the next move, or `rigor-plugin-author`
|
|
174
|
+
if the review surfaced a *new* capability the plugin should grow.
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Plugin best-practices checklist
|
|
2
|
+
|
|
3
|
+
Score a plugin concern by concern. Each row is **smell → modern
|
|
4
|
+
replacement → authority**. The authority column names the binding
|
|
5
|
+
surface (`docs/internal-spec/*` or the ADR); when it disagrees with
|
|
6
|
+
this file, it wins.
|
|
7
|
+
|
|
8
|
+
`M` = mechanical (byte-identical diagnostics expected; oracle-gated).
|
|
9
|
+
`D` = design-level (may change behaviour; validate empirically).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Manifest & config defaults (ADR-40) — `M`
|
|
14
|
+
|
|
15
|
+
**Smell:**
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
DEFAULT_ROUTES_FILE = "config/routes.yml"
|
|
19
|
+
config_schema: { "routes_file" => :string }
|
|
20
|
+
# …
|
|
21
|
+
@routes_file = config.fetch("routes_file", DEFAULT_ROUTES_FILE)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Modern:**
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
config_schema: { "routes_file" => { kind: :string, default: "config/routes.yml" } }
|
|
28
|
+
# …
|
|
29
|
+
@routes_file = config["routes_file"] # default merged under user config
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`Base#config` merges `manifest.config_defaults` beneath the user config,
|
|
33
|
+
so the plugin reads the key directly and the `DEFAULT_*` constant goes
|
|
34
|
+
away. Keep a constant only where a value needs *validation the merged
|
|
35
|
+
default cannot express* (e.g. an allow-listed `severity` that must fall
|
|
36
|
+
back when a user supplies a bad value).
|
|
37
|
+
|
|
38
|
+
**Authority:** `docs/internal-spec/plugin.md` § "Declared config
|
|
39
|
+
defaults — `config_schema` `{ kind:, default: }`".
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 2. AST-walk ownership (ADR-37 / ADR-52) — `D`
|
|
44
|
+
|
|
45
|
+
**Smell:** a hand-rolled traversal for per-node checks —
|
|
46
|
+
`root.compact_child_nodes.each { … }`, a bespoke `Walker` that recurses
|
|
47
|
+
the tree, or a `#diagnostics_for_file` that re-walks to find call sites.
|
|
48
|
+
|
|
49
|
+
**Modern:** declare `node_rule(Prism::CallNode) { |node, scope, path| … }`
|
|
50
|
+
and let the engine own the single per-file walk. For a two-pass
|
|
51
|
+
(collect-then-validate) plugin, add `node_file_context { |root, scope| … }`
|
|
52
|
+
— it runs once before the node rules and threads a file-local value in
|
|
53
|
+
as the rule block's fourth argument.
|
|
54
|
+
|
|
55
|
+
**Keep `#diagnostics_for_file`** only for genuinely whole-file
|
|
56
|
+
diagnostics a per-node walk cannot express — see concern 5 for the case
|
|
57
|
+
where a stateful whole-file walk is *required*, not lazy.
|
|
58
|
+
|
|
59
|
+
**"Genuinely whole-file" is a sharp line — apply it, don't defer to
|
|
60
|
+
precedent.** A per-*class* or per-*def* contract check (does this class
|
|
61
|
+
define `#get`? does its body return the contracted type?) IS
|
|
62
|
+
node-expressible — migrate it to `node_rule(Prism::ClassNode)` /
|
|
63
|
+
`node_rule(Prism::DefNode)`, even if a shipped production plugin still
|
|
64
|
+
uses `#diagnostics_for_file` + a hand-rolled `class_nodes` walk for the
|
|
65
|
+
same job (`rigor-hanami`'s ADR-28 check half does — an equivalent,
|
|
66
|
+
older shape, not a reason to keep a new copy hand-rolled). The genuine
|
|
67
|
+
whole-file case is a diagnostic whose *identity or count is not tied to
|
|
68
|
+
any one node* — e.g. `rigor-routes`'s "routes file failed to load"
|
|
69
|
+
warning, which must fire exactly once per file (or run) even on a file
|
|
70
|
+
with zero matching nodes. That cannot be a node rule; a per-class check
|
|
71
|
+
can.
|
|
72
|
+
|
|
73
|
+
**Authority:** `docs/internal-spec/plugin.md` §§ "Node-scoped rules —
|
|
74
|
+
`node_rule`", "`node_file_context`".
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. Return-type & narrowing hooks (ADR-37 / ADR-52 / ADR-80) — `M`/`D`
|
|
79
|
+
|
|
80
|
+
**Smell:** `flow_contribution_for` (deleted in ADR-52 WD3 — *defining it
|
|
81
|
+
now raises `ArgumentError`*), or `type_specifier` (the pre-ADR-80 name).
|
|
82
|
+
|
|
83
|
+
**Modern:** `dynamic_return(receivers:/methods:/file_methods:)` to
|
|
84
|
+
*supply* a return type, `narrowing_facts(methods:)` to supply
|
|
85
|
+
post-return narrowing facts. `type_specifier` survives as a
|
|
86
|
+
deprecating alias removed in 0.3.0 — rename to `narrowing_facts`.
|
|
87
|
+
|
|
88
|
+
The gate resolves after `#init` when passed a callable
|
|
89
|
+
(`methods: -> { [@method_name] }`), so config-derived method names work.
|
|
90
|
+
|
|
91
|
+
**Authority:** `docs/internal-spec/plugin.md` § "Return-type and
|
|
92
|
+
narrowing contributions". `M` for the pure rename; `D` if you are adding
|
|
93
|
+
a contribution the plugin did not have.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 4. Authoring helpers (ADR-37 / ADR-60 WD4) — `M`
|
|
98
|
+
|
|
99
|
+
The single richest source of drift. Each helper replaces a hand-rolled
|
|
100
|
+
shape and is expected to be diagnostics-preserving.
|
|
101
|
+
|
|
102
|
+
| Smell | Modern | Authority |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `Rigor::Analysis::Diagnostic.new(line:, column: loc.start_column + 1, …)` where a node exists | `#diagnostic(node, path:, message:, severity:, rule:)` (or `location: node.message_loc` for a sub-span) | plugin.md § "Positioning a diagnostic — `#diagnostic`" |
|
|
105
|
+
| `violations.map { |v| diagnostic(node, message: v.message, …) }` | `#diagnostics_for(violations, path:, node:)` — duck-types `#message` / `#node` / `#severity` / `#rule` / `#location` | plugin.md § author helpers (ADR-60 WD4) |
|
|
106
|
+
| A hand-rolled `levenshtein` / `closest_*` "did you mean" | `Rigor::Plugin::Base.suggest(name, candidates)` (`DidYouMean::SpellChecker`) — a **class** method, callable from an `Analyzer` too | plugin.md § "`Base.suggest`" |
|
|
107
|
+
| `@table ||= cache_for(id).call` + a multi-`rescue` ladder + an `@load_error` ivar | `#producer_value(id, params:)` (memoised incl. nil) + `#producer_error(id)` (the rescued exception, for a tailored message) | plugin-cache-producers.md § "Invalidation contract" |
|
|
108
|
+
| `@x_resolved` flag guarding `services.fact_store.read` | `#read_fact(plugin_id:, name:)` — nil-inclusive memo, retires the flag | plugin.md § author helpers (ADR-60 WD4) |
|
|
109
|
+
| Hand-parsing a `Prism::SymbolNode#value` / string literal | `Rigor::Source::Literals` | plugin.md § "Extracting argument literals" |
|
|
110
|
+
| A private reimplementation of a target library (own inflector, own pure helper) | Call the library's safe methods directly — `Plugin::Inflector` over real `ActiveSupport::Inflector` (ADR-39) | plugin.md § "Target-library invocation" |
|
|
111
|
+
|
|
112
|
+
**Note on tailored load-error messages:** `#producer_value` rescues
|
|
113
|
+
every `StandardError` into `#producer_error`, so a plugin that wants
|
|
114
|
+
class-specific messages ("not found" vs "failed to parse" vs
|
|
115
|
+
access-denied) switches on `producer_error(id)`'s class when building
|
|
116
|
+
the load-error diagnostic — cleaner than an inline rescue ladder and
|
|
117
|
+
still message-preserving. A *file-level* load-error (line 1, no node)
|
|
118
|
+
legitimately keeps a direct `Diagnostic.new` — `#diagnostic` needs a
|
|
119
|
+
node to position at.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 5. Engine collaboration vs reimplementation — `D` (read the trap)
|
|
124
|
+
|
|
125
|
+
**Principle (rigor-pattern):** do not re-implement a fact the engine
|
|
126
|
+
already computes. If you need "is this a literal string?" / "what type
|
|
127
|
+
did inference give this expression?", read `Scope#type_of(node)` rather
|
|
128
|
+
than tracking it yourself. `rigor-pattern` reads the engine's
|
|
129
|
+
`LiteralStringFolding` result back instead of propagating strings by
|
|
130
|
+
hand.
|
|
131
|
+
|
|
132
|
+
**The trap (rigor-units) — when a hand-rolled binding map is NOT
|
|
133
|
+
redundant:** the `Scope` handed to the **diagnostics** side
|
|
134
|
+
(`#diagnostics_for_file` / a `node_rule`) is the **seed entry scope**.
|
|
135
|
+
`Scope#type_of` re-evaluates a *self-contained* expression on demand
|
|
136
|
+
(`scope.type_of(100.kilometers)` folds through the plugin's own
|
|
137
|
+
`dynamic_return`), but it carries **no flow-accumulated local
|
|
138
|
+
bindings**: for `speed = distance / time`, `scope.type_of(distance)` is
|
|
139
|
+
`untyped`, because the entry scope never bound the earlier assignment.
|
|
140
|
+
Only the **flow scope** handed to a `dynamic_return` block resolves such
|
|
141
|
+
locals. So a diagnostics-side check that must follow a dimension /
|
|
142
|
+
type across statements legitimately keeps its own single-pass binding
|
|
143
|
+
map — deleting it and reaching for `scope.type_of` collapses
|
|
144
|
+
cross-statement propagation.
|
|
145
|
+
|
|
146
|
+
**How to tell which case you are in:** if the value you need lives *at
|
|
147
|
+
the call site you are inspecting* (a literal argument, a self-contained
|
|
148
|
+
sub-expression) → read `Scope#type_of`. If it lives *in an earlier
|
|
149
|
+
statement's local binding* and you are on the diagnostics side → the
|
|
150
|
+
engine will not hand it to you; keep the binding map, and document why.
|
|
151
|
+
**Validate empirically before deleting state:** run the integration
|
|
152
|
+
spec after the change; a wave of failures on multi-statement fixtures is
|
|
153
|
+
this trap.
|
|
154
|
+
|
|
155
|
+
**Authority:** the reasoning is recorded in
|
|
156
|
+
`docs/notes/20260704-examples-plugin-modernization-survey.md` (the
|
|
157
|
+
`rigor-units` section) and the `rigor-units` class comment itself.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 6. Cache producers (ADR-60 WD3) — `M`/`D`
|
|
162
|
+
|
|
163
|
+
**Smell:** a "prime the read before `cache_for` so the digest is
|
|
164
|
+
captured" comment, or a producer that globs a directory but does not
|
|
165
|
+
declare `watch:`.
|
|
166
|
+
|
|
167
|
+
**Modern:** record-and-validate — the `io_boundary.read_file` inside the
|
|
168
|
+
`producer` block is captured into the dependency descriptor *after* the
|
|
169
|
+
block runs, so there is nothing to prime. A producer that reads a *set*
|
|
170
|
+
of files by glob declares `watch:` so file *additions* invalidate too.
|
|
171
|
+
|
|
172
|
+
**Authority:** `docs/internal-spec/plugin-cache-producers.md` §§
|
|
173
|
+
"Invalidation contract", "`producer(... watch:)`".
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 7. Manifest-field hygiene (ADR-60 WD1 / WD2) — `M`
|
|
178
|
+
|
|
179
|
+
**Smell:** `external_files:` (never wired — removed in ADR-60 WD1);
|
|
180
|
+
`BlockAsMethod verbs:` (renamed → `method_names:`); `NestedClassTemplate
|
|
181
|
+
name_arg_position:` (renamed → `symbol_arg_position:`).
|
|
182
|
+
|
|
183
|
+
**Modern:** drop `external_files:`; use the renamed keys.
|
|
184
|
+
|
|
185
|
+
**Authority:** `docs/internal-spec/plugin.md` § "`Rigor::Plugin::Manifest`"
|
|
186
|
+
and `macro-substrate.md`.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 8. Documentation freshness — `M`
|
|
191
|
+
|
|
192
|
+
**Smell:** "the former `flow_contribution_for` hook was removed"
|
|
193
|
+
archaeology in a README / class comment (a reader who never knew the
|
|
194
|
+
deleted hook does not need it named); pinned version references that no
|
|
195
|
+
longer carry meaning ("introduced in v0.0.9").
|
|
196
|
+
|
|
197
|
+
**Modern:** describe the *current* mechanism directly. Keep a historical
|
|
198
|
+
note only where a reader migrating an old plugin genuinely needs it —
|
|
199
|
+
and put that in a CHANGELOG / migration note, not the class docstring.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 9. Verification (ADR-43) — always
|
|
204
|
+
|
|
205
|
+
- `rigor check <plugin>/lib` — the ADR-43 contract self-check resolves
|
|
206
|
+
the plugin's inherited `Plugin::Base` calls and warns on contract
|
|
207
|
+
misuse. MUST be clean; fix the cause, never disable the rule.
|
|
208
|
+
- `rigor plugins --strict` — the plugin still activates.
|
|
209
|
+
- `rigor plugins --capabilities` — `node_rule_types` /
|
|
210
|
+
`dynamic_return_receivers` / `type_specifier_methods` reflect the
|
|
211
|
+
declarations.
|
|
212
|
+
- The integration / unit spec — green, and byte-identical across every
|
|
213
|
+
mechanical step.
|
|
214
|
+
- In the monorepo: `make check-plugins` + `make verify`.
|
|
@@ -20,6 +20,21 @@ All `rigor-*` plugins ship **bundled inside the `rigortype` gem** — no
|
|
|
20
20
|
separate install. Enabling one is just adding its id to `plugins:` in
|
|
21
21
|
`.rigor.dist.yml`.
|
|
22
22
|
|
|
23
|
+
## First: load the version-current copy
|
|
24
|
+
|
|
25
|
+
This skill's exact commands, flags, and config keys drift between Rigor
|
|
26
|
+
releases, so follow the copy that ships with the **installed** Rigor rather
|
|
27
|
+
than any vendored or frozen copy of this file. Get the complete current
|
|
28
|
+
procedure in one call:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
rigor skill --full rigor-plugin-tune
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
35
|
+
copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
|
|
36
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
37
|
+
|
|
23
38
|
## When to use
|
|
24
39
|
|
|
25
40
|
- The project added (or removed) a gem since it was onboarded.
|
|
@@ -43,9 +58,13 @@ dependencies in `Gemfile.lock`.
|
|
|
43
58
|
### Phase 2 — match against the bundled catalogue
|
|
44
59
|
|
|
45
60
|
The authoritative, current list of bundled plugins (the count drifts as
|
|
46
|
-
new ones land)
|
|
61
|
+
new ones land) comes from the **installed** Rigor itself — read it there,
|
|
62
|
+
not from a web page that may describe a different version:
|
|
47
63
|
|
|
48
|
-
|
|
64
|
+
```sh
|
|
65
|
+
rigor docs manual/plugins/README # the catalogue of every bundled plugin, offline
|
|
66
|
+
rigor plugins # what is enabled in THIS project right now
|
|
67
|
+
```
|
|
49
68
|
|
|
50
69
|
For each Gemfile.lock dependency, check whether a bundled `rigor-<gem>`
|
|
51
70
|
plugin exists and is **not** already in `plugins:`. Common matches:
|
|
@@ -23,6 +23,22 @@ chapter for the install channels (`mise` recommended). This skill
|
|
|
23
23
|
references only public CLI flags and config keys — the same surface
|
|
24
24
|
`rigor --help` documents.
|
|
25
25
|
|
|
26
|
+
## First: load the version-current copy
|
|
27
|
+
|
|
28
|
+
This skill's step detail lives in its `references/` files, and its exact
|
|
29
|
+
commands, flags, and config keys drift between Rigor releases — so follow
|
|
30
|
+
the copy that ships with the **installed** Rigor rather than any vendored
|
|
31
|
+
or frozen copy of this file. Get the complete current procedure (body + all
|
|
32
|
+
references, inline) in one call:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
rigor skill --full rigor-project-init
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
39
|
+
copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
|
|
40
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
41
|
+
|
|
26
42
|
## Phase 0 — When to use this skill
|
|
27
43
|
|
|
28
44
|
Trigger when the user says "set up Rigor here", "configure rigor for
|
|
@@ -16,6 +16,21 @@ type. This skill *acts* on that surfacing under the discipline that keeps
|
|
|
16
16
|
Rigor false-positive-safe: protection goes up, and not one line of
|
|
17
17
|
working code starts reporting a new diagnostic.
|
|
18
18
|
|
|
19
|
+
## First: load the version-current copy
|
|
20
|
+
|
|
21
|
+
This skill's exact commands, flags, carrier syntax, and rule ids drift
|
|
22
|
+
between Rigor releases, so follow the copy that ships with the **installed**
|
|
23
|
+
Rigor rather than any vendored or frozen copy of this file. Get the
|
|
24
|
+
complete current procedure in one call:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
rigor skill --full rigor-protection-uplift
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
31
|
+
copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
|
|
32
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
33
|
+
|
|
19
34
|
## When to use
|
|
20
35
|
|
|
21
36
|
- A user wants to raise how much of their code Rigor can actually catch
|
|
@@ -18,6 +18,21 @@ community RBS collection at
|
|
|
18
18
|
[`ruby/gem_rbs_collection`](https://github.com/ruby/gem_rbs_collection);
|
|
19
19
|
this skill wires it into the project.
|
|
20
20
|
|
|
21
|
+
## First: load the version-current copy
|
|
22
|
+
|
|
23
|
+
This skill's exact commands, flags, and config keys drift between Rigor
|
|
24
|
+
releases, so follow the copy that ships with the **installed** Rigor rather
|
|
25
|
+
than any vendored or frozen copy of this file. Get the complete current
|
|
26
|
+
procedure in one call:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
rigor skill --full rigor-rbs-setup
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If you already loaded this skill *via* `rigor skill` you have the current
|
|
33
|
+
copy — just proceed. If `rigor` is not on `PATH`, this task needs it: run
|
|
34
|
+
**`rigor-next-steps`** to install Rigor first, then come back.
|
|
35
|
+
|
|
21
36
|
## When to use
|
|
22
37
|
|
|
23
38
|
- `rigor check` ends with `info: N gem(s) in Gemfile.lock have no RBS
|