rigortype 0.1.12 → 0.1.13
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/lib/rigor/analysis/check_rules.rb +96 -3
- data/lib/rigor/cli/skill_command.rb +170 -0
- data/lib/rigor/cli.rb +9 -1
- data/lib/rigor/configuration/severity_profile.rb +3 -0
- data/lib/rigor/scope.rb +14 -0
- data/lib/rigor/version.rb +1 -1
- data/sig/rigor/scope.rbs +1 -0
- data/skills/rigor-baseline-reduce/SKILL.md +100 -0
- data/skills/rigor-baseline-reduce/references/01-classify.md +107 -0
- data/skills/rigor-baseline-reduce/references/02-fix-or-suppress.md +133 -0
- data/skills/rigor-plugin-author/SKILL.md +95 -0
- data/skills/rigor-plugin-author/references/01-plan-and-scaffold.md +195 -0
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +155 -0
- data/skills/rigor-plugin-author/references/03-test-and-ship.md +163 -0
- data/skills/rigor-project-init/SKILL.md +129 -0
- data/skills/rigor-project-init/references/01-detect.md +101 -0
- data/skills/rigor-project-init/references/02-configure.md +185 -0
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +168 -0
- data/skills/rigor-project-init/references/04-sig-uplift.md +171 -0
- metadata +14 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# 03 — Test and ship
|
|
2
|
+
|
|
3
|
+
Covers **Phase 3** — testing the plugin from outside the rigor
|
|
4
|
+
monorepo, pinning against the pre-1.0 contract, and shipping.
|
|
5
|
+
|
|
6
|
+
## Testing — drive the public CLI
|
|
7
|
+
|
|
8
|
+
The rigor monorepo's plugin specs use an internal `plugin_helpers.rb`
|
|
9
|
+
(`run_plugin`, `plugin_diagnostics`). That helper is **not part of
|
|
10
|
+
the published `rigortype` surface** — an external plugin cannot use
|
|
11
|
+
it. Test against what you *do* have: the `rigor` CLI.
|
|
12
|
+
|
|
13
|
+
The robust pattern is a **fixture project + `rigor check --format
|
|
14
|
+
json`**. It exercises the real load path, the real walker, the real
|
|
15
|
+
diagnostic pipeline — exactly what your users get — and works
|
|
16
|
+
identically under RSpec or Minitest.
|
|
17
|
+
|
|
18
|
+
### Fixture layout
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
spec/fixtures/basic/ (or test/fixtures/basic/)
|
|
22
|
+
├── .rigor.yml # plugins: [rigor-<id>]
|
|
23
|
+
└── sample.rb # code that exercises the plugin
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The fixture's `.rigor.yml` activates the plugin and scopes `paths:`
|
|
27
|
+
to the sample file:
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
paths:
|
|
31
|
+
- sample.rb
|
|
32
|
+
plugins:
|
|
33
|
+
- rigor-<id>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### RSpec
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
# spec/rigor_<id>_spec.rb
|
|
40
|
+
require "json"
|
|
41
|
+
require "open3"
|
|
42
|
+
|
|
43
|
+
RSpec.describe "rigor-<id>" do
|
|
44
|
+
def diagnostics_for(fixture)
|
|
45
|
+
dir = File.expand_path("fixtures/#{fixture}", __dir__)
|
|
46
|
+
out, _err, _status = Open3.capture3(
|
|
47
|
+
"bundle", "exec", "rigor", "check", "--format", "json", chdir: dir
|
|
48
|
+
)
|
|
49
|
+
JSON.parse(out).fetch("diagnostics")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "flags a dimensional mismatch" do
|
|
53
|
+
diags = diagnostics_for("basic")
|
|
54
|
+
rule = diags.map { |d| d["rule"] }
|
|
55
|
+
expect(rule).to include("plugin.<id>.dimension-mismatch")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Minitest
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
# test/rigor_<id>_test.rb
|
|
64
|
+
require "minitest/autorun"
|
|
65
|
+
require "json"
|
|
66
|
+
require "open3"
|
|
67
|
+
|
|
68
|
+
class RigorPluginTest < Minitest::Test
|
|
69
|
+
def diagnostics_for(fixture)
|
|
70
|
+
dir = File.expand_path("fixtures/#{fixture}", __dir__)
|
|
71
|
+
out, = Open3.capture3("bundle", "exec", "rigor", "check",
|
|
72
|
+
"--format", "json", chdir: dir)
|
|
73
|
+
JSON.parse(out).fetch("diagnostics")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_flags_dimensional_mismatch
|
|
77
|
+
rules = diagnostics_for("basic").map { |d| d["rule"] }
|
|
78
|
+
assert_includes rules, "plugin.<id>.dimension-mismatch"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`rigor check --format json` emits one object per run; the
|
|
84
|
+
`"diagnostics"` array carries `path` / `line` / `column` / `message`
|
|
85
|
+
/ `severity` / `rule`. Assert on `rule` and `severity` — they are the
|
|
86
|
+
stable fields. Avoid asserting on exact `message` wording; it changes
|
|
87
|
+
between Rigor releases.
|
|
88
|
+
|
|
89
|
+
### Unit-test the pure parts directly
|
|
90
|
+
|
|
91
|
+
Dispatch tables, parsers, and dimension maths inside the plugin are
|
|
92
|
+
plain Ruby — test them as ordinary objects, no `rigor` process
|
|
93
|
+
needed:
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
it "dispatches distance / time to speed" do
|
|
97
|
+
result = Rigor::Plugin::Units::MethodTable.dispatch(
|
|
98
|
+
receiver: :distance, method: :/, args: [:time]
|
|
99
|
+
)
|
|
100
|
+
expect(result.dimension).to eq(:speed)
|
|
101
|
+
end
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Split the suite: fast unit tests for the logic, a few fixture-driven
|
|
105
|
+
CLI tests for the end-to-end wiring.
|
|
106
|
+
|
|
107
|
+
## Version pinning — the pre-1.0 contract
|
|
108
|
+
|
|
109
|
+
The plugin contract is **not frozen until `rigortype` v0.2.0** (see
|
|
110
|
+
SKILL.md). Concretely:
|
|
111
|
+
|
|
112
|
+
- Gemspec / Gemfile: `rigortype` `>= 0.1.0, < 0.2.0`. Never `>= 0.1`
|
|
113
|
+
alone — that floats across the contract-changing v0.2.0 boundary.
|
|
114
|
+
- Your plugin's own version is normal semver, independent of
|
|
115
|
+
`rigortype`'s.
|
|
116
|
+
- When you bump the `rigortype` pin to a new minor, **re-run the
|
|
117
|
+
full test suite** — the walker hook signature or the `Diagnostic` /
|
|
118
|
+
type-carrier shapes may have changed. The fixture CLI tests are
|
|
119
|
+
what catch a contract drift.
|
|
120
|
+
- State the supported `rigortype` range in the README so users do
|
|
121
|
+
not pair the plugin with an incompatible Rigor.
|
|
122
|
+
|
|
123
|
+
When v0.2.0 lands, re-pin to the stable range and ordinary
|
|
124
|
+
compatibility rules apply.
|
|
125
|
+
|
|
126
|
+
## README
|
|
127
|
+
|
|
128
|
+
A plugin README should carry:
|
|
129
|
+
|
|
130
|
+
1. **What it does** — the DSL / framework it teaches Rigor, and one
|
|
131
|
+
sample diagnostic.
|
|
132
|
+
2. **Install** — `gem "rigor-<id>"` (or the path-gem snippet for a
|
|
133
|
+
project-private plugin).
|
|
134
|
+
3. **Activate** — the `.rigor.yml` `plugins:` entry, plus any
|
|
135
|
+
`config:` keys and `signature_paths:`.
|
|
136
|
+
4. **Compatibility** — the supported `rigortype` version range, and
|
|
137
|
+
the pre-1.0-contract caveat.
|
|
138
|
+
5. **License.**
|
|
139
|
+
|
|
140
|
+
## Ship
|
|
141
|
+
|
|
142
|
+
**Standalone gem** — `gem build rigor-<id>.gemspec` then
|
|
143
|
+
`gem push`. Tag the release. Announce the supported `rigortype`
|
|
144
|
+
range.
|
|
145
|
+
|
|
146
|
+
**Project-private plugin** — nothing to publish. Commit it with the
|
|
147
|
+
app (the `rigor-plugin/` path-gem directory, or the `RUBYLIB` file).
|
|
148
|
+
Make sure CI runs `rigor check` so the plugin stays
|
|
149
|
+
wired and the fixture tests run.
|
|
150
|
+
|
|
151
|
+
Either way, if the plugin uncovered a gap that *should* be core
|
|
152
|
+
Rigor behaviour — or if you hit a plugin-contract rough edge — report
|
|
153
|
+
it: <https://github.com/rigortype/rigor/issues>. External plugin
|
|
154
|
+
authors are the main source of pre-v0.2.0 contract feedback.
|
|
155
|
+
|
|
156
|
+
## Output of this module — plugin shipped
|
|
157
|
+
|
|
158
|
+
- A test suite: fast unit tests + fixture-driven `rigor check`
|
|
159
|
+
tests, in RSpec or Minitest.
|
|
160
|
+
- A `rigortype` pin tight to `< 0.2.0`.
|
|
161
|
+
- A README stating the compatibility range.
|
|
162
|
+
- The plugin published as a gem, or committed project-private with
|
|
163
|
+
CI wired.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rigor-project-init
|
|
3
|
+
description: |
|
|
4
|
+
Onboard a project to Rigor type-checking from scratch: detect the stack, choose an adoption mode (baseline vs. strict), select plugins, write `.rigor.dist.yml`, then snapshot a baseline or commit to a zero-diagnostic gate. Triggers: "set up Rigor in this project", "configure rigor for X", "add type checking", or running `rigor check` in a Gemfile directory with no `.rigor.yml`. NOT for reducing an existing baseline (use rigor-baseline-reduce) or authoring a 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 Project Init
|
|
12
|
+
|
|
13
|
+
End-to-end onboarding for a project that has never run Rigor. The
|
|
14
|
+
output is a committed `.rigor.dist.yml`, an explicit adoption mode,
|
|
15
|
+
and — if the project chooses it — a `.rigor-baseline.yml` snapshot.
|
|
16
|
+
|
|
17
|
+
This skill is for **users adopting Rigor on their own project**. It
|
|
18
|
+
uses the published `rigor` executable, installed standalone — Rigor
|
|
19
|
+
is a tool, not a library, so it does **not** go in the project's
|
|
20
|
+
`Gemfile`. See the manual's
|
|
21
|
+
[Installing Rigor](../../docs/manual/01-installation.md)
|
|
22
|
+
chapter for the install channels (`mise` recommended). This skill
|
|
23
|
+
references only public CLI flags and config keys — the same surface
|
|
24
|
+
`rigor --help` documents.
|
|
25
|
+
|
|
26
|
+
## Phase 0 — When to use this skill
|
|
27
|
+
|
|
28
|
+
Trigger when the user says "set up Rigor here", "configure rigor for
|
|
29
|
+
this app", "add type checking", or runs `rigor check` in a project
|
|
30
|
+
that has no `.rigor.yml` / `.rigor.dist.yml`.
|
|
31
|
+
|
|
32
|
+
Do NOT trigger for:
|
|
33
|
+
|
|
34
|
+
- **An existing baseline the user wants to shrink** — that is the
|
|
35
|
+
`rigor-baseline-reduce` skill.
|
|
36
|
+
- **Writing a Rigor plugin** for the project's own DSL /
|
|
37
|
+
metaprogramming — that is the `rigor-plugin-author` skill. (This
|
|
38
|
+
skill *points at* plugin authoring as an escalation in Phase 7;
|
|
39
|
+
it does not do it.)
|
|
40
|
+
- **Tweaking an already-configured project** — ordinary edits to an
|
|
41
|
+
existing `.rigor.yml`; no onboarding pipeline needed.
|
|
42
|
+
|
|
43
|
+
## Note — plugin installation
|
|
44
|
+
|
|
45
|
+
All `rigor-*` plugins ship **bundled inside the `rigortype` gem**.
|
|
46
|
+
No separate installation is needed. Listing a plugin under
|
|
47
|
+
`plugins:` in `.rigor.dist.yml` is sufficient to activate it.
|
|
48
|
+
The project's `Gemfile` is untouched by this workflow.
|
|
49
|
+
See [`references/02-configure.md`](references/02-configure.md)
|
|
50
|
+
§ "No separate installation needed" for detail.
|
|
51
|
+
|
|
52
|
+
## The central decision — adoption mode
|
|
53
|
+
|
|
54
|
+
A mature Ruby codebase routinely reports hundreds to thousands of
|
|
55
|
+
diagnostics the first time `rigor check` runs. Most are not fresh
|
|
56
|
+
bugs: some are real-but-empirically-safe (`T | nil` that production
|
|
57
|
+
always initialises), some are project style, a minority are genuine
|
|
58
|
+
latent bugs. Forcing every site to zero before adoption blocks
|
|
59
|
+
adoption entirely.
|
|
60
|
+
|
|
61
|
+
So **before writing any config, present the user with two modes**
|
|
62
|
+
and let them choose. The mode drives the severity profile, whether a
|
|
63
|
+
baseline is generated, and how Phase 7 frames the leftover
|
|
64
|
+
diagnostics.
|
|
65
|
+
|
|
66
|
+
| | **Acknowledge mode** (baseline adoption) | **Strict mode** (no compromise) |
|
|
67
|
+
| --- | --- | --- |
|
|
68
|
+
| Goal | Adopt Rigor now; make sure *ordinary coding does not increase* the diagnostic count. | Drive the project to zero outstanding diagnostics and keep it there. |
|
|
69
|
+
| Today's diagnostics | Snapshotted into `.rigor-baseline.yml`; suppressed as long as the count does not grow. | All surfaced; every one is fixed or consciously suppressed. |
|
|
70
|
+
| Hard-to-fix diagnostics | Left in the baseline. The project **trusts its test / spec suite** to cover runtime correctness for those sites — the static `T | nil` reading is worst-case-sound, the suite proves the worst case is not hit. | Fixed, or annotated `# rigor:disable <rule>` with an author-intent reason at the specific line. No blanket suppression. |
|
|
71
|
+
| `severity_profile` | `lenient` (or `balanced` for a small project). | `strict`. |
|
|
72
|
+
| Best for | Mature codebases; incremental adoption; teams that want the regression guard without a big upfront fix. | New / small projects; libraries; teams that want the maximum guarantee and have the budget to reach zero. |
|
|
73
|
+
| New diagnostics later | Surface immediately — anything beyond the baseline envelope is a regression. | Surface immediately — there is no envelope; every diagnostic is live. |
|
|
74
|
+
|
|
75
|
+
Both modes give the same core guarantee: **a change that introduces
|
|
76
|
+
a new diagnostic is caught.** They differ only in what happens to the
|
|
77
|
+
diagnostics that exist *today*. Acknowledge mode parenthesises them
|
|
78
|
+
behind a baseline and leans on the test suite; strict mode refuses to
|
|
79
|
+
parenthesise anything.
|
|
80
|
+
|
|
81
|
+
If the project's first `rigor check` reports more than ~100 errors,
|
|
82
|
+
recommend acknowledge mode as the default and let the user override.
|
|
83
|
+
Below that, either mode is reasonable — ask.
|
|
84
|
+
|
|
85
|
+
## Phase outline
|
|
86
|
+
|
|
87
|
+
| Phase | What | Reference |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| 1 | Detect the project shape (Gemfile / Gemfile.lock walk). | [`references/01-detect.md`](references/01-detect.md) |
|
|
90
|
+
| 2 | Present the two adoption modes; record the user's choice. | (this file — § "The central decision") |
|
|
91
|
+
| 3 | Select the plugin set matching the detected stack. | [`references/01-detect.md`](references/01-detect.md) |
|
|
92
|
+
| 4 | Write `.rigor.dist.yml` — severity profile follows the mode. Verify activation with `rigor plugins`. | [`references/02-configure.md`](references/02-configure.md) |
|
|
93
|
+
| 5 | Generate initial RBS sigs; uplift attr_reader precision with `--params=observed`. | [`references/04-sig-uplift.md`](references/04-sig-uplift.md) |
|
|
94
|
+
| 6 | Run `rigor triage --format json` to diagnose the diagnostic stream. | [`references/03-baseline-and-bugs.md`](references/03-baseline-and-bugs.md) |
|
|
95
|
+
| 7 | Acknowledge mode only — generate the baseline and wire `baseline:`. | [`references/03-baseline-and-bugs.md`](references/03-baseline-and-bugs.md) |
|
|
96
|
+
| 8 | Surface likely real bugs; offer the two escalation paths. | [`references/03-baseline-and-bugs.md`](references/03-baseline-and-bugs.md) |
|
|
97
|
+
|
|
98
|
+
Load each reference when you reach its phase. Phases run in order;
|
|
99
|
+
the only branch is Phase 7 (acknowledge mode runs it, strict mode
|
|
100
|
+
skips it). Phase 5 is also optional if the project already has a
|
|
101
|
+
committed `sig/` directory.
|
|
102
|
+
|
|
103
|
+
## Reading order — modules
|
|
104
|
+
|
|
105
|
+
| Module | Read | Covers |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| 1 | [`references/01-detect.md`](references/01-detect.md) | **Phases 1 + 3.** Gemfile / Gemfile.lock walk → framework family. The plugin-recommendation table (Rails / dry-rb / Sinatra / RSpec / plain Ruby). RBS-collection presence check. |
|
|
108
|
+
| 2 | [`references/02-configure.md`](references/02-configure.md) | **Phase 4.** Severity-profile choice tied to the mode. The `.rigor.dist.yml` template and every key it uses. The `.rigor.dist.yml` vs `.rigor.yml` convention. |
|
|
109
|
+
| 3 | [`references/04-sig-uplift.md`](references/04-sig-uplift.md) | **Phase 5.** `rigor sig-gen --write` baseline. `rigor sig-gen --params=observed --write` attr_reader precision uplift. Handling residual `untyped` methods. Committing `sig/`. |
|
|
110
|
+
| 4 | [`references/03-baseline-and-bugs.md`](references/03-baseline-and-bugs.md) | **Phases 6–8.** `rigor triage` as the diagnosis layer. `rigor baseline generate` + wiring `baseline:`. Surfacing likely real bugs. The two escalation paths — write a project plugin, or open a Rigor issue. |
|
|
111
|
+
|
|
112
|
+
## Escalation paths (Phase 7 preview)
|
|
113
|
+
|
|
114
|
+
Some diagnostic clusters are neither a quick fix nor honest baseline
|
|
115
|
+
material. Two of them have a dedicated answer this skill hands off to:
|
|
116
|
+
|
|
117
|
+
- **Application-specific metaprogramming** — a project DSL,
|
|
118
|
+
`define_method` factory, or in-house macro that Rigor cannot follow
|
|
119
|
+
produces a cluster of `call.undefined-method`. The durable fix is a
|
|
120
|
+
**project-private Rigor plugin** that teaches Rigor the DSL. Hand
|
|
121
|
+
off to the `rigor-plugin-author` skill.
|
|
122
|
+
- **An external gem Rigor does not understand** — a dependency ships
|
|
123
|
+
no RBS and Rigor has no built-in coverage for it. Try
|
|
124
|
+
`rbs collection install` first; if that gem genuinely needs Rigor
|
|
125
|
+
support, **open an issue on the Rigor project** asking for it:
|
|
126
|
+
<https://github.com/rigortype/rigor/issues>.
|
|
127
|
+
|
|
128
|
+
Neither is a Phase 7 obligation — they are options to *offer* the
|
|
129
|
+
user when the triage report points at one of these causes.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# 01 — Detect the project shape & select plugins
|
|
2
|
+
|
|
3
|
+
Covers **Phase 1** (detect) and **Phase 3** (plugin selection). Run
|
|
4
|
+
Phase 2 — the mode choice — from `SKILL.md` between them.
|
|
5
|
+
|
|
6
|
+
## Phase 1 — Detect
|
|
7
|
+
|
|
8
|
+
Read two files at the project root. Do not run code; just parse them.
|
|
9
|
+
|
|
10
|
+
### `Gemfile` — framework family
|
|
11
|
+
|
|
12
|
+
Scan the `gem "…"` lines for the markers below. A project can match
|
|
13
|
+
more than one row (a Rails app with RSpec and Sidekiq matches three).
|
|
14
|
+
|
|
15
|
+
| Marker gems | Family |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `rails`, `railties`, `actionpack`, `activerecord` | Rails |
|
|
18
|
+
| `sinatra` | Sinatra |
|
|
19
|
+
| `dry-types`, `dry-struct`, `dry-schema`, `dry-validation` | dry-rb |
|
|
20
|
+
| `rspec`, `rspec-core` | RSpec test suite |
|
|
21
|
+
| `sorbet`, `sorbet-runtime` | Sorbet-typed |
|
|
22
|
+
| none of the above | plain Ruby |
|
|
23
|
+
|
|
24
|
+
Also note per-gem markers that have their own plugin: `devise`,
|
|
25
|
+
`pundit`, `sidekiq`.
|
|
26
|
+
|
|
27
|
+
### `Gemfile.lock` — versions & RBS state
|
|
28
|
+
|
|
29
|
+
- Read the **locked versions** of the framework gems — a plugin
|
|
30
|
+
recommendation can depend on a major version.
|
|
31
|
+
- Check for `rbs_collection.lock.yaml` at the project root AND whether
|
|
32
|
+
`.gem_rbs_collection/` exists alongside it.
|
|
33
|
+
- **Lockfile present, `.gem_rbs_collection/` present** → the collection
|
|
34
|
+
is installed; Rigor will auto-detect and consume it via
|
|
35
|
+
`rbs_collection.auto_detect: true` (the default).
|
|
36
|
+
- **Lockfile present, `.gem_rbs_collection/` absent** → the lockfile
|
|
37
|
+
was generated but the gems were never downloaded. Rigor loads the
|
|
38
|
+
lockfile but finds no RBS files. Note this for Phase 6: if triage
|
|
39
|
+
reports `gem-without-rbs` hints for gems that appear in
|
|
40
|
+
`rbs_collection.yaml`, run `rbs collection install` first and
|
|
41
|
+
re-run triage.
|
|
42
|
+
- **Both absent** → note it; Phase 6's triage may recommend
|
|
43
|
+
`rbs collection install` if `gem-without-rbs` hints appear.
|
|
44
|
+
|
|
45
|
+
### Path scope
|
|
46
|
+
|
|
47
|
+
Note the conventional source roots so Phase 4 can set `paths:`:
|
|
48
|
+
|
|
49
|
+
- Rails → `app`, `lib`.
|
|
50
|
+
- gem / library → `lib`.
|
|
51
|
+
- plain app → `lib`, or the directory holding the code.
|
|
52
|
+
|
|
53
|
+
`spec/` and `test/` are normally **excluded** from `paths:` — they
|
|
54
|
+
are checked differently and inflate the diagnostic count. `vendor/`
|
|
55
|
+
and `tmp/` are always excluded.
|
|
56
|
+
|
|
57
|
+
## Phase 3 — Plugin selection
|
|
58
|
+
|
|
59
|
+
Propose a plugin set from the detected families. Present it to the
|
|
60
|
+
user as a list they can trim — do not silently enable everything.
|
|
61
|
+
|
|
62
|
+
| Family | Recommended plugins |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| Rails | `rigor-actionpack`, `rigor-activerecord`, `rigor-actionmailer`, `rigor-rails-routes`, `rigor-rails-i18n`, plus `rigor-activesupport-core-ext` (almost always needed — see below) |
|
|
65
|
+
| dry-rb | `rigor-dry-types`, `rigor-dry-struct`, and `rigor-dry-schema` / `rigor-dry-validation` when those gems are present |
|
|
66
|
+
| Sinatra | `rigor-sinatra` |
|
|
67
|
+
| RSpec | `rigor-rspec` |
|
|
68
|
+
| Devise / Pundit / Sidekiq present | `rigor-devise` / `rigor-pundit` / `rigor-sidekiq` |
|
|
69
|
+
| Sorbet present | `rigor-sorbet` (ingests existing `sig` blocks / RBI as type sources) |
|
|
70
|
+
| plain Ruby | none required — the core analyzer covers it |
|
|
71
|
+
|
|
72
|
+
The current production-plugin catalogue is the authority for which
|
|
73
|
+
plugins exist and how each is named / installed:
|
|
74
|
+
<https://github.com/rigortype/rigor/blob/master/plugins/README.md>.
|
|
75
|
+
The set drifts as new plugins land — consult that page rather than
|
|
76
|
+
treating the table above as exhaustive.
|
|
77
|
+
|
|
78
|
+
### `rigor-activesupport-core-ext` — the common Rails gap
|
|
79
|
+
|
|
80
|
+
ActiveSupport monkey-patches the core classes (`3.days`,
|
|
81
|
+
`5.minutes`, `"x".squish`, `Time.current`, …). Without the
|
|
82
|
+
`rigor-activesupport-core-ext` bundle, every such call reports
|
|
83
|
+
`call.undefined-method` — on a real Rails app this is the single
|
|
84
|
+
largest diagnostic cluster (a measured Mastodon run: ~365 of 489
|
|
85
|
+
diagnostics were exactly this). Phase 5's `rigor triage` flags it as
|
|
86
|
+
hint `activesupport-core-ext`.
|
|
87
|
+
|
|
88
|
+
`rigor-activesupport-core-ext` is a **plugin** (an RBS-bundle plugin
|
|
89
|
+
— it contributes signatures, not diagnostics). Activate it like any
|
|
90
|
+
other: list it under `plugins:`. No `signature_paths:` wiring is
|
|
91
|
+
needed — the plugin ships its own `sig/`. Include it for any
|
|
92
|
+
Rails-family project.
|
|
93
|
+
|
|
94
|
+
## Output of this module
|
|
95
|
+
|
|
96
|
+
- A framework-family list.
|
|
97
|
+
- A proposed, user-trimmed plugin set.
|
|
98
|
+
- The `paths:` / `exclude:` scope for Phase 4.
|
|
99
|
+
- Whether an RBS collection already exists.
|
|
100
|
+
|
|
101
|
+
Carry these into Phase 4 ([`02-configure.md`](02-configure.md)).
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# 02 — Write the configuration
|
|
2
|
+
|
|
3
|
+
Covers **Phase 4**. Inputs: the plugin set + path scope from
|
|
4
|
+
[`01-detect.md`](01-detect.md), and the adoption mode chosen in
|
|
5
|
+
Phase 2.
|
|
6
|
+
|
|
7
|
+
## `.rigor.dist.yml` vs `.rigor.yml`
|
|
8
|
+
|
|
9
|
+
Rigor reads both. The convention:
|
|
10
|
+
|
|
11
|
+
- **`.rigor.dist.yml`** — the committed, shared project config. This
|
|
12
|
+
skill writes this file.
|
|
13
|
+
- **`.rigor.yml`** — an optional, gitignored per-developer override.
|
|
14
|
+
Leave it for individuals to create; do not write one here.
|
|
15
|
+
|
|
16
|
+
When both exist, `.rigor.yml` takes precedence. Writing the shared
|
|
17
|
+
config as `.rigor.dist.yml` lets a contributor opt out locally (for
|
|
18
|
+
example, run without the baseline) without touching the committed
|
|
19
|
+
file.
|
|
20
|
+
|
|
21
|
+
## Severity profile — follows the mode
|
|
22
|
+
|
|
23
|
+
The `severity_profile:` key re-stamps every rule's severity. Set it
|
|
24
|
+
from the Phase 2 mode:
|
|
25
|
+
|
|
26
|
+
| Mode | `severity_profile` | Why |
|
|
27
|
+
| --- | --- | --- |
|
|
28
|
+
| Acknowledge, >100 errors on first run | `lenient` | Most rules become warnings; the baseline carries the residue. The point is the regression guard, not a wall of errors. |
|
|
29
|
+
| Acknowledge, small project | `balanced` | The default. Errors stay errors; the baseline is small enough to live with. |
|
|
30
|
+
| Strict | `strict` | Promotes borderline rules to errors. Paired with no baseline, every diagnostic is a live gate. |
|
|
31
|
+
|
|
32
|
+
`balanced` is the built-in default — omit the key to get it.
|
|
33
|
+
|
|
34
|
+
## No separate installation needed
|
|
35
|
+
|
|
36
|
+
All plugins ship **bundled inside the `rigortype` gem**. The
|
|
37
|
+
`plugins:` list in the config is all that is needed to activate
|
|
38
|
+
them — the plugin loader runs `require "rigor-<id>"` from within
|
|
39
|
+
the gem's own load path. No Gemfile entry, no `bundle install`,
|
|
40
|
+
no separate gem channel.
|
|
41
|
+
|
|
42
|
+
**The `rigortype` gem itself stays out of the project's
|
|
43
|
+
`Gemfile`** — install it standalone per the manual's
|
|
44
|
+
[Installing Rigor](../../docs/manual/01-installation.md) chapter
|
|
45
|
+
(`mise use gem:rigortype` is the recommended channel). The
|
|
46
|
+
project's Gemfile is untouched by this workflow.
|
|
47
|
+
|
|
48
|
+
## The template
|
|
49
|
+
|
|
50
|
+
Write `.rigor.dist.yml` at the project root. A Rails app in
|
|
51
|
+
acknowledge mode looks like:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
# .rigor.dist.yml — Rigor configuration (committed; shared).
|
|
55
|
+
# Generated by the rigor-project-init workflow.
|
|
56
|
+
|
|
57
|
+
# Match the Ruby version in .ruby-version or the Gemfile `ruby "x.y"` line.
|
|
58
|
+
# Rigor defaults to 4.0; set this explicitly if the project targets Ruby < 4.0.
|
|
59
|
+
target_ruby: "3.4"
|
|
60
|
+
|
|
61
|
+
paths:
|
|
62
|
+
- app
|
|
63
|
+
- lib
|
|
64
|
+
|
|
65
|
+
exclude:
|
|
66
|
+
- vendor
|
|
67
|
+
- tmp
|
|
68
|
+
|
|
69
|
+
plugins:
|
|
70
|
+
- rigor-actionpack
|
|
71
|
+
- rigor-activerecord
|
|
72
|
+
- rigor-actionmailer
|
|
73
|
+
- rigor-rails-routes
|
|
74
|
+
- rigor-rails-i18n
|
|
75
|
+
# An RBS-bundle plugin — ships ActiveSupport core_ext signatures,
|
|
76
|
+
# no signature_paths: wiring needed (see 01-detect.md).
|
|
77
|
+
- rigor-activesupport-core-ext
|
|
78
|
+
|
|
79
|
+
severity_profile: lenient
|
|
80
|
+
|
|
81
|
+
# Phase 6 (acknowledge mode) appends this line after generating the
|
|
82
|
+
# baseline. Strict mode leaves it out entirely.
|
|
83
|
+
# baseline: .rigor-baseline.yml
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Existing `sig/` directory
|
|
87
|
+
|
|
88
|
+
If the project already has a `sig/` directory (from Steep, `rbs_rails`,
|
|
89
|
+
or handwritten annotations), wire it into `signature_paths:` so Rigor
|
|
90
|
+
consumes it. Phase 5 (sig uplift) can be skipped, but the paths must
|
|
91
|
+
still be declared — Rigor does not auto-detect `sig/`:
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
signature_paths:
|
|
95
|
+
- sig/handwritten # adjust to the layout in your project
|
|
96
|
+
- sig/generated
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
List only directories that contain `.rbs` files or subdirectories of
|
|
100
|
+
them. Rigor walks each path recursively. If the sig layout is a single
|
|
101
|
+
flat `sig/` directory, use `- sig` instead.
|
|
102
|
+
|
|
103
|
+
A strict-mode plain-Ruby gem is shorter:
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
paths:
|
|
107
|
+
- lib
|
|
108
|
+
severity_profile: strict
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Key reference
|
|
112
|
+
|
|
113
|
+
Only the keys this skill needs. `rigor --help` and the project
|
|
114
|
+
handbook document the full surface.
|
|
115
|
+
|
|
116
|
+
| Key | Meaning |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| `paths:` | Directories Rigor analyses. Source roots only — not `spec/` / `test/`. |
|
|
119
|
+
| `exclude:` | Paths removed from the `paths:` walk. |
|
|
120
|
+
| `plugins:` | Plugin ids to activate (the Phase 3 set). |
|
|
121
|
+
| `signature_paths:` | Extra RBS source **directories** (paths, not gem names; resolved relative to the config file). Use it for the project's own local `sig/` if it has one. RBS-bundle *plugins* like `rigor-activesupport-core-ext` ship their own `sig/` and need no entry here — list them under `plugins:`. |
|
|
122
|
+
| `severity_profile:` | `lenient` / `balanced` / `strict`. See the table above. |
|
|
123
|
+
| `severity_overrides:` | Per-rule severity tweaks. Leave empty at init; the baseline-reduce workflow tunes it later. |
|
|
124
|
+
| `baseline:` | Path to the baseline file. **Only acknowledge mode sets it**, and only in Phase 6 *after* the file exists. Per Rigor's no-magic rule, a `.rigor-baseline.yml` on disk does nothing until this key names it. |
|
|
125
|
+
| `pre_eval:` | Project files Rigor walks before per-file inference — used to register in-project monkey-patches. Leave empty at init; Phase 7 may suggest it. |
|
|
126
|
+
| `dependencies.source_inference:` | Opt-in inference for gems shipping no RBS. Leave empty at init; Phase 7 may suggest it. |
|
|
127
|
+
|
|
128
|
+
## Do not write the baseline yet
|
|
129
|
+
|
|
130
|
+
Phase 4 writes the config with the `baseline:` line **commented out
|
|
131
|
+
or absent**. The baseline file does not exist until Phase 6, and a
|
|
132
|
+
`baseline:` pointing at a missing file is an error. Phase 6 writes
|
|
133
|
+
the file and uncomments / appends the line in one step.
|
|
134
|
+
|
|
135
|
+
Strict mode never adds `baseline:` at all.
|
|
136
|
+
|
|
137
|
+
## Verify plugin activation (`rigor plugins`)
|
|
138
|
+
|
|
139
|
+
Before proceeding to sig uplift, **run `rigor plugins`** from the
|
|
140
|
+
project root to confirm every entry under `plugins:` actually
|
|
141
|
+
loaded. The activation surface is the part of the configuration
|
|
142
|
+
most prone to silent failure — a typoed gem name, a missing
|
|
143
|
+
third-party plugin gem, or running rigor from a different cwd
|
|
144
|
+
(so a different `.rigor.yml` is discovered) all leave plugins
|
|
145
|
+
inert, which the per-file diagnostic stream then attributes to
|
|
146
|
+
"missing types" rather than to a config gap.
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
rigor plugins
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Expected: every entry shows `[OK ]`, the `loaded: N` count
|
|
153
|
+
matches the entries in `plugins:`, and `load-error: 0`. The
|
|
154
|
+
report also lists each plugin's `signature_paths:` (with a
|
|
155
|
+
per-directory `.rbs` file count) and any `open_receivers:` /
|
|
156
|
+
`owns_receivers:` / `produces:` / `consumes:` / macro substrate
|
|
157
|
+
contributions — useful for confirming the plugin is contributing
|
|
158
|
+
what you expect.
|
|
159
|
+
|
|
160
|
+
If any entry shows `[ERR]`, read the `load error:` line:
|
|
161
|
+
|
|
162
|
+
| Error fragment | Cause | Fix |
|
|
163
|
+
| --- | --- | --- |
|
|
164
|
+
| `could not load plugin gem "rigor-foo"` | The gem is not on the load path. Either misspelled in `plugins:`, or a third-party plugin (per ADR-31 WD4) that is not installed in the rigor runtime environment. | Check spelling against the catalogue in `01-detect.md`; for third-party plugins, install via the same channel that installed `rigortype` (mise / gem install). |
|
|
165
|
+
| `did not register any plugin via Rigor::Plugin.register` | The require succeeded but the gem did not call `Rigor::Plugin.register`. Usually a version mismatch (older / forked rigor-foo) or a partial install. | Reinstall the plugin gem; verify the version matches `rigortype`'s. |
|
|
166
|
+
| `signature path "..." is not a directory` | The bundled `sig/` directory is missing — a packaging bug in the plugin gem. | File an issue against the plugin gem's repo. |
|
|
167
|
+
| `plugin "foo" config invalid: ...` | The `config:` block under the plugin entry has a typo or wrong value type. | Compare against the plugin's documented `config_schema:`. |
|
|
168
|
+
|
|
169
|
+
Re-run `rigor plugins` until `load-error: 0`. Add `--strict` to
|
|
170
|
+
the invocation when you want it to exit 1 (the canonical CI gate
|
|
171
|
+
shape):
|
|
172
|
+
|
|
173
|
+
```sh
|
|
174
|
+
rigor plugins --strict
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Output of this module
|
|
178
|
+
|
|
179
|
+
A committed `.rigor.dist.yml` with `paths:`, `exclude:`,
|
|
180
|
+
`plugins:`, and `severity_profile:` set — and no active
|
|
181
|
+
`baseline:` line. `rigor plugins` reports every entry loaded,
|
|
182
|
+
zero load errors. No Gemfile changes; plugins are bundled inside
|
|
183
|
+
`rigortype`.
|
|
184
|
+
|
|
185
|
+
Proceed to Phase 5 ([`04-sig-uplift.md`](04-sig-uplift.md)).
|