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.
@@ -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)).