rigortype 0.2.6 → 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/manual/02-cli-reference.md +2 -1
- data/docs/manual/08-skills.md +21 -0
- data/lib/rigor/cli/coverage_command.rb +42 -10
- data/lib/rigor/cli/skill_command.rb +52 -1
- data/lib/rigor/environment/rbs_loader.rb +28 -0
- data/lib/rigor/inference/statement_evaluator.rb +0 -4
- 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/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-sorbet/lib/rigor/plugin/sorbet.rb +33 -76
- 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 +16 -0
- 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 +7 -4
|
@@ -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
|
|
@@ -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
|
|
@@ -17,6 +17,22 @@ sharpness exposes. This skill adopts the upgrade without either blindly
|
|
|
17
17
|
regenerating the baseline (which buries genuine new catches) or treating
|
|
18
18
|
every new line as a regression.
|
|
19
19
|
|
|
20
|
+
## First: load the version-current copy
|
|
21
|
+
|
|
22
|
+
This skill's exact commands, flags, and config keys drift between Rigor
|
|
23
|
+
releases, so follow the copy that ships with the **installed** Rigor rather
|
|
24
|
+
than any vendored or frozen copy of this file — doubly so here, since you
|
|
25
|
+
just changed the version this skill is meant to track. Get the complete
|
|
26
|
+
current procedure in one call:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
rigor skill --full rigor-upgrade
|
|
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
|
+
|
|
20
36
|
## When to use
|
|
21
37
|
|
|
22
38
|
- You just ran `mise use gem:rigortype` / `gem update rigortype` and want
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rigortype
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Rigor contributors
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 1980-01-
|
|
10
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: language_server-protocol
|
|
@@ -886,6 +886,7 @@ files:
|
|
|
886
886
|
- skills/rigor-baseline-reduce/references/02-fix-or-suppress.md
|
|
887
887
|
- skills/rigor-ci-setup/SKILL.md
|
|
888
888
|
- skills/rigor-doctor/SKILL.md
|
|
889
|
+
- skills/rigor-doctor/references/01-checks.md
|
|
889
890
|
- skills/rigor-editor-setup/SKILL.md
|
|
890
891
|
- skills/rigor-mcp-setup/SKILL.md
|
|
891
892
|
- skills/rigor-monkeypatch-resolve/SKILL.md
|
|
@@ -894,6 +895,8 @@ files:
|
|
|
894
895
|
- skills/rigor-plugin-author/references/01-plan-and-scaffold.md
|
|
895
896
|
- skills/rigor-plugin-author/references/02-walker-and-types.md
|
|
896
897
|
- skills/rigor-plugin-author/references/03-test-and-ship.md
|
|
898
|
+
- skills/rigor-plugin-review/SKILL.md
|
|
899
|
+
- skills/rigor-plugin-review/references/01-best-practices-checklist.md
|
|
897
900
|
- skills/rigor-plugin-tune/SKILL.md
|
|
898
901
|
- skills/rigor-project-init/SKILL.md
|
|
899
902
|
- skills/rigor-project-init/references/01-detect.md
|
|
@@ -909,7 +912,7 @@ licenses:
|
|
|
909
912
|
metadata:
|
|
910
913
|
bug_tracker_uri: https://github.com/rigortype/rigor/issues
|
|
911
914
|
source_code_uri: https://github.com/rigortype/rigor
|
|
912
|
-
documentation_uri: https://github.com/rigortype/rigor/tree/
|
|
915
|
+
documentation_uri: https://github.com/rigortype/rigor/tree/master/docs
|
|
913
916
|
rubygems_mfa_required: 'true'
|
|
914
917
|
rdoc_options: []
|
|
915
918
|
require_paths:
|
|
@@ -958,7 +961,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
958
961
|
- !ruby/object:Gem::Version
|
|
959
962
|
version: '0'
|
|
960
963
|
requirements: []
|
|
961
|
-
rubygems_version:
|
|
964
|
+
rubygems_version: 3.7.2
|
|
962
965
|
specification_version: 4
|
|
963
966
|
summary: Inference-first static analysis for Ruby.
|
|
964
967
|
test_files: []
|