rubocop-legion 0.1.7 → 0.1.9
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/.rubocop.yml +9 -0
- data/CHANGELOG.md +8 -0
- data/CLAUDE.md +17 -75
- data/config/default.yml +27 -0
- data/lib/rubocop/cop/legion/extension/every_actor_requires_time.rb +5 -1
- data/lib/rubocop/cop/legion/framework/no_direct_dispatch.rb +109 -0
- data/lib/rubocop/cop/legion/framework/no_inline_setting_defaults.rb +110 -0
- data/lib/rubocop/cop/legion/framework/no_shape_duck_typing.rb +100 -0
- data/lib/rubocop/cop/legion/framework/no_underscore_prefixed_kwargs.rb +89 -0
- data/lib/rubocop/cop/legion/llm/no_loop_do.rb +41 -0
- data/lib/rubocop/cop/legion/llm/rescue_log_level.rb +61 -0
- data/lib/rubocop/cop/legion/llm/settings_access_path.rb +62 -0
- data/lib/rubocop/cop/legion/llm/taxonomy_enum.rb +77 -0
- data/lib/rubocop/legion/version.rb +1 -1
- data/lib/rubocop-legion.rb +10 -0
- metadata +10 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 88034e8168ad8e5634a580f3a07ca2f030bd46be31b6c9607dbc64a28935bcd0
|
|
4
|
+
data.tar.gz: 353647b902a3271fe17809769674778b528eb3e97dda8704934addf80b69b397
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0b7d05703e9bd6762628d25f082a738fba9972f66eda2b650ab5e3c0dace6b3569df16c754f935f8f62caf861eaf3984018c3a4f3f73de01cc0871fcf91cfebd
|
|
7
|
+
data.tar.gz: d598e5090c4ba1242ec371fd2d3b935412f9fe8c4e24fe907a9e18bd6052999568bed0c252a983559e4b33e1c0502a724e02df5244db46b98c5cdad899f62e93
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.8] - 2026-06-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- New cop `Legion/Framework/NoUnderscorePrefixedKwargs`: ban `_foo:` keyword arguments and `**_rest` splats; autocorrects to plain names / `**opts`
|
|
7
|
+
- New cop `Legion/Framework/NoInlineSettingDefaults`: flag `Legion::Settings[...] || <literal>` and shadow-default patterns (tunables belong in `settings.rb`)
|
|
8
|
+
- New cop `Legion/Framework/NoDirectDispatch`: ban `*_direct` methods on `Legion::LLM` modules (every pipeline exit must route through the governed pipeline)
|
|
9
|
+
- New cop `Legion/Framework/NoShapeDuckTyping` (disabled by default): flag `respond_to?` on canonical shape methods and string-key fallback access on canonical bodies
|
|
10
|
+
|
|
3
11
|
## [0.1.7] - 2026-03-29
|
|
4
12
|
|
|
5
13
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -1,98 +1,52 @@
|
|
|
1
1
|
# rubocop-legion
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## What is This?
|
|
6
|
-
|
|
7
|
-
Custom RuboCop plugin gem for the LegionIO ecosystem. Provides 47 AST-based cops across 6 departments. Uses the new RuboCop Plugin API (1.72+, lint_roller-based) with auto-discovery via gemspec metadata.
|
|
8
|
-
|
|
9
|
-
**GitHub**: https://github.com/LegionIO/rubocop-legion
|
|
10
|
-
**RubyGems**: https://rubygems.org/gems/rubocop-legion
|
|
11
|
-
**License**: MIT
|
|
3
|
+
Custom RuboCop plugin gem for the LegionIO ecosystem. Provides 47 AST-based cops across 6 departments. Uses the RuboCop Plugin API (1.72+, lint_roller-based) with auto-discovery via gemspec metadata.
|
|
12
4
|
|
|
13
5
|
## Shared Config Profiles
|
|
14
6
|
|
|
15
|
-
The gem ships shared `.rubocop.yml` profiles so repos don't duplicate config:
|
|
16
|
-
|
|
17
7
|
- `config/base.yml` — all shared settings (AllCops, Layout, Metrics, Style, Naming, Performance, ThreadSafety)
|
|
18
|
-
- `config/lex.yml` — inherits base, adds plugins
|
|
19
|
-
- `config/core.yml` — inherits base, adds plugins
|
|
20
|
-
|
|
21
|
-
### Bundled Plugins
|
|
8
|
+
- `config/lex.yml` — inherits base, adds plugins + `ParameterLists Max: 8`
|
|
9
|
+
- `config/core.yml` — inherits base, adds plugins + `ParameterLists Max: 10, CountKeywordArgs: false`
|
|
22
10
|
|
|
23
|
-
Both profiles load `rubocop-performance` (52 cops) and `rubocop-thread_safety` (6 cops) as runtime dependencies.
|
|
11
|
+
Both profiles load `rubocop-performance` (52 cops) and `rubocop-thread_safety` (6 cops) as runtime dependencies.
|
|
24
12
|
|
|
25
13
|
**LEX repos**: `inherit_gem: { rubocop-legion: config/lex.yml }`
|
|
26
14
|
**Core repos**: `inherit_gem: { rubocop-legion: config/core.yml }`
|
|
27
15
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
## Cop Scoping
|
|
31
|
-
|
|
32
|
-
Cops are scoped by gem type — no per-repo configuration needed:
|
|
33
|
-
|
|
34
|
-
- **Universal** (9 cops): Fire on all LegionIO gems
|
|
35
|
-
- **Library-specific** (6 cops): Fire on all gems but only trigger when using Sequel, Sinatra, Thor, Faraday, or cache
|
|
36
|
-
- **LEX-only** (31 cops): Scoped to `lib/legion/extensions/**/*.rb` via Include directive — never fire on core `legion-*` libraries
|
|
37
|
-
|
|
38
|
-
## Departments and Cops
|
|
39
|
-
|
|
40
|
-
### Universal — 10 cops
|
|
16
|
+
## Cop Departments
|
|
41
17
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
18
|
+
| Department | Count | Scope |
|
|
19
|
+
|------------|-------|-------|
|
|
20
|
+
| ConstantSafety | 4 | Universal |
|
|
21
|
+
| RescueLogging | 3 | Universal |
|
|
22
|
+
| Singleton | 1 | Universal |
|
|
23
|
+
| Framework | 8 | Universal + Library-specific |
|
|
24
|
+
| HelperMigration | 13 | LEX-only |
|
|
25
|
+
| Extension | 18 | LEX-only |
|
|
47
26
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
- `EagerSequelModel` — `Sequel::Model(:table)` at require time
|
|
51
|
-
- `SinatraHostAuth` — Sinatra 4.0+ `set :host_authorization`
|
|
52
|
-
- `ThorReservedRun` — Thor 1.5+ reserves `run`
|
|
53
|
-
- `FaradayXmlMiddleware` — Faraday 2.0+ removed `:xml`
|
|
54
|
-
- `CacheTimeCoercion` — Time→String after cache round-trip
|
|
55
|
-
- `ApiStringKeys` — `Legion::JSON.load` returns symbol keys (scoped to `lib/legion/extensions/**/*.rb`)
|
|
56
|
-
|
|
57
|
-
### LEX-Only — 29 cops
|
|
58
|
-
|
|
59
|
-
- **HelperMigration** (13): `DirectLogging`, `OldLoggingMethods`, `DirectJson`, `DirectCache`, `DirectLocalCache`, `DirectCrypt`, `DirectData`, `DirectLlm`, `DirectTransport`, `DirectKnowledge`, `RequireDefinedGuard` (all auto-fix) — use per-extension helpers, not global singletons. `LoggingGuard` (no auto-fix) — remove unnecessary `respond_to?(:log_warn)` / `defined?(Legion::Logging)` guards. `DefinedTransportGuard` (no auto-fix) — use `transport_connected?` instead of `defined?(Legion::Transport)`.
|
|
60
|
-
- **Extension** (18): `ActorSingularModule` (auto-fix), `RunnerPluralModule` (auto-fix), `CoreExtendGuard` (auto-fix), `RunnerMustBeModule`, `RunnerIncludeHelpers`, `ActorInheritance`, `EveryActorRequiresTime`, `SelfContainedActorRunnerClass`, `HookMissingRunnerClass`, `AbsorberMissingPattern`, `AbsorberMissingAbsorbMethod`, `DefinitionCallMismatched`, `RunnerReturnHash`, `SettingsKeyMethod` (auto-fix), `SettingsBracketMultiArg` (auto-fix), `LlmAskKwargs`, `ActorEnabledSideEffects`, `DataRequiredWithoutMigrations`.
|
|
27
|
+
Cops are scoped by gem type — no per-repo configuration needed. LEX-only cops fire on `lib/legion/extensions/**/*.rb` via Include directive.
|
|
61
28
|
|
|
62
29
|
## Architecture
|
|
63
30
|
|
|
64
31
|
```
|
|
65
32
|
rubocop-legion/
|
|
66
33
|
├── lib/
|
|
67
|
-
│ ├── rubocop-legion.rb # Entry point
|
|
34
|
+
│ ├── rubocop-legion.rb # Entry point
|
|
68
35
|
│ └── rubocop/
|
|
69
|
-
│ ├── legion.rb # Namespace declarations
|
|
70
36
|
│ ├── legion/
|
|
71
|
-
│ │ ├── version.rb
|
|
72
37
|
│ │ └── plugin.rb # LintRoller::Plugin (auto-discovery)
|
|
73
38
|
│ └── cop/legion/
|
|
74
39
|
│ ├── helper_migration/ # 13 cops (lex-only)
|
|
75
40
|
│ ├── constant_safety/ # 4 cops (universal)
|
|
76
41
|
│ ├── singleton/ # 1 cop (universal)
|
|
77
42
|
│ ├── rescue_logging/ # 3 cops (universal)
|
|
78
|
-
│ ├── framework/ #
|
|
79
|
-
│ └── extension/ #
|
|
43
|
+
│ ├── framework/ # 8 cops (universal + library-specific)
|
|
44
|
+
│ └── extension/ # 18 cops (lex-only)
|
|
80
45
|
├── config/
|
|
81
46
|
│ └── default.yml # All cop defaults, Include/Exclude scoping
|
|
82
47
|
└── spec/ # mirrors lib/ structure
|
|
83
48
|
```
|
|
84
49
|
|
|
85
|
-
## Key Implementation Details
|
|
86
|
-
|
|
87
|
-
- Plugin entry point: `RuboCop::Legion::Plugin` (LintRoller-based, registered via gemspec metadata `default_lint_roller_plugin`)
|
|
88
|
-
- All cops inherit from `RuboCop::Cop::Base`
|
|
89
|
-
- Auto-correctable cops use `extend AutoCorrector`
|
|
90
|
-
- AST matching via `def_node_matcher` and `def_node_search`
|
|
91
|
-
- Specs use `RuboCop::RSpec::Support` with `:config` shared context and `expect_offense`/`expect_correction`
|
|
92
|
-
- `BareRescue` and `NoCapture` skip rescue modifiers (inline `rescue`) to avoid syntax corruption
|
|
93
|
-
- `NoCapture` has no auto-correct to prevent correction loop with `Lint/UselessAssignment`
|
|
94
|
-
- `SilentCapture` skips `_`-prefixed variables (Ruby unused convention)
|
|
95
|
-
|
|
96
50
|
## Development
|
|
97
51
|
|
|
98
52
|
```bash
|
|
@@ -100,15 +54,3 @@ bundle install
|
|
|
100
54
|
bundle exec rspec # 350 specs
|
|
101
55
|
bundle exec rubocop # Self-linting
|
|
102
56
|
```
|
|
103
|
-
|
|
104
|
-
## Common Per-Repo Overrides
|
|
105
|
-
|
|
106
|
-
```yaml
|
|
107
|
-
# Repos using Faraday JSON middleware (string keys, not Legion::JSON symbol keys)
|
|
108
|
-
Legion/Framework/ApiStringKeys:
|
|
109
|
-
Enabled: false
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
---
|
|
113
|
-
|
|
114
|
-
**Maintained By**: Matthew Iverson (@Esity)
|
data/config/default.yml
CHANGED
|
@@ -336,3 +336,30 @@ Legion/Extension/DefinitionCallMismatched:
|
|
|
336
336
|
Enabled: true
|
|
337
337
|
Severity: error
|
|
338
338
|
VersionAdded: '0.1.4'
|
|
339
|
+
|
|
340
|
+
# Legion/Framework — N×N routing guard cops (Phase 6 enforcement)
|
|
341
|
+
|
|
342
|
+
Legion/Framework/NoUnderscorePrefixedKwargs:
|
|
343
|
+
Description: 'Ban underscore-prefixed kwargs and `**_rest` splats. Use plain kwarg, defaulted kwarg, or `**opts`.'
|
|
344
|
+
Enabled: true
|
|
345
|
+
Severity: warning
|
|
346
|
+
VersionAdded: '0.1.8'
|
|
347
|
+
|
|
348
|
+
Legion/Framework/NoInlineSettingDefaults:
|
|
349
|
+
Description: 'Flag `Legion::Settings[...] || <literal>` and shadow-default patterns. Defaults belong in `settings.rb`.'
|
|
350
|
+
Enabled: true
|
|
351
|
+
Severity: warning
|
|
352
|
+
VersionAdded: '0.1.8'
|
|
353
|
+
|
|
354
|
+
Legion/Framework/NoDirectDispatch:
|
|
355
|
+
Description: 'Ban `_direct` methods on `Legion::LLM` modules. All inference routes through the governed pipeline.'
|
|
356
|
+
Enabled: true
|
|
357
|
+
Severity: error
|
|
358
|
+
VersionAdded: '0.1.8'
|
|
359
|
+
|
|
360
|
+
Legion/Framework/NoShapeDuckTyping:
|
|
361
|
+
Description: 'Flag `respond_to?` on canonical shape methods and string-key fallback access. Disabled by default.'
|
|
362
|
+
Enabled: false
|
|
363
|
+
Severity: convention
|
|
364
|
+
VersionAdded: '0.1.8'
|
|
365
|
+
Safe: false
|
|
@@ -59,7 +59,11 @@ module RuboCop
|
|
|
59
59
|
|
|
60
60
|
node.body.each_node(:send).any? do |send_node|
|
|
61
61
|
send_node.method_name == :time && send_node.receiver.nil?
|
|
62
|
-
end
|
|
62
|
+
end || defines_time_method?(node)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def defines_time_method?(node)
|
|
66
|
+
node.body.each_node(:def).any? { |def_node| def_node.method_name == :time }
|
|
63
67
|
end
|
|
64
68
|
end
|
|
65
69
|
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Framework
|
|
7
|
+
# Bans defining methods matching `/_direct\z/` on `Legion::LLM` modules.
|
|
8
|
+
# Per G16, all pipeline bypasses (`chat_direct`, `embed_direct`,
|
|
9
|
+
# `structured_direct`) are removed — internal callers route through the
|
|
10
|
+
# governed pipeline with an appropriate profile.
|
|
11
|
+
#
|
|
12
|
+
# No auto-correct is provided because the fix requires routing through
|
|
13
|
+
# the inference pipeline.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# # bad
|
|
17
|
+
# module Legion::LLM
|
|
18
|
+
# def chat_direct(message:)
|
|
19
|
+
# # bypasses metering, audit, ledger
|
|
20
|
+
# end
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# # bad
|
|
24
|
+
# module Legion::LLM
|
|
25
|
+
# def self.embed_direct(text:)
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# # good
|
|
30
|
+
# module Legion::LLM
|
|
31
|
+
# def chat(message:)
|
|
32
|
+
# # routes through pipeline
|
|
33
|
+
# end
|
|
34
|
+
# end
|
|
35
|
+
class NoDirectDispatch < Base
|
|
36
|
+
MSG = 'Method `%<name>s` matches `/_direct\\z/` on a `Legion::LLM` module. ' \
|
|
37
|
+
'Pipeline bypasses are not allowed — route through the governed pipeline.'
|
|
38
|
+
|
|
39
|
+
DIRECT_PATTERN = /_direct\z/
|
|
40
|
+
|
|
41
|
+
def on_def(node)
|
|
42
|
+
return unless node.method_name.to_s.match?(DIRECT_PATTERN)
|
|
43
|
+
return unless inside_legion_llm?(node)
|
|
44
|
+
|
|
45
|
+
add_offense(node.loc.name, message: format(MSG, name: node.method_name))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def on_defs(node)
|
|
49
|
+
return unless node.method_name.to_s.match?(DIRECT_PATTERN)
|
|
50
|
+
return unless inside_legion_llm?(node)
|
|
51
|
+
|
|
52
|
+
add_offense(node.loc.name, message: format(MSG, name: node.method_name))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def inside_legion_llm?(node)
|
|
58
|
+
node.each_ancestor(:module, :class).any? do |ancestor|
|
|
59
|
+
legion_llm_module?(ancestor)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def legion_llm_module?(mod_node)
|
|
64
|
+
return false unless mod_node.module_type?
|
|
65
|
+
|
|
66
|
+
# Check compact form: Legion::LLM or Legion::LLM::Something
|
|
67
|
+
ident = mod_node.identifier
|
|
68
|
+
return true if compact_legion_llm?(ident)
|
|
69
|
+
|
|
70
|
+
# Check nested form: module Legion; module LLM; ...
|
|
71
|
+
# The identifier is just `LLM` but parent is `module Legion`
|
|
72
|
+
return true if nested_legion_llm?(mod_node)
|
|
73
|
+
|
|
74
|
+
false
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def compact_legion_llm?(ident)
|
|
78
|
+
parts = const_parts(ident)
|
|
79
|
+
parts.length >= 2 && parts[0] == :Legion && parts[1] == :LLM
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def nested_legion_llm?(mod_node)
|
|
83
|
+
return false unless mod_node.identifier.const_type?
|
|
84
|
+
return false unless mod_node.identifier.children.last == :LLM
|
|
85
|
+
|
|
86
|
+
# Check if parent module is `Legion`
|
|
87
|
+
parent = mod_node.parent
|
|
88
|
+
return false unless parent&.module_type?
|
|
89
|
+
return false unless parent&.identifier&.const_type?
|
|
90
|
+
|
|
91
|
+
parent.identifier.children.last == :Legion
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def const_parts(node)
|
|
95
|
+
return [] unless node&.const_type?
|
|
96
|
+
|
|
97
|
+
child = node.children.first
|
|
98
|
+
name = node.children.last
|
|
99
|
+
if child&.const_type?
|
|
100
|
+
const_parts(child) + [name]
|
|
101
|
+
else
|
|
102
|
+
[name]
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Framework
|
|
7
|
+
# Detects `Legion::Settings[...]` reads combined with inline literal
|
|
8
|
+
# fallbacks (`|| <literal>`) or shadow-default patterns
|
|
9
|
+
# (`x = N unless x.positive?`). Per G13, all defaults belong in
|
|
10
|
+
# `settings.rb` — inline fallbacks are dead code after
|
|
11
|
+
# `register_defaults!` merge.
|
|
12
|
+
#
|
|
13
|
+
# No auto-correct is provided because the fix requires moving the
|
|
14
|
+
# literal into the appropriate `*_defaults` group in settings.rb.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# # bad
|
|
18
|
+
# max_retries = Legion::Settings[:llm][:max_retries] || 3
|
|
19
|
+
# timeout = Legion::Settings[:timeout] || 30
|
|
20
|
+
#
|
|
21
|
+
# # bad (shadow default)
|
|
22
|
+
# max_retries = Legion::Settings[:llm][:max_retries]
|
|
23
|
+
# max_retries = 200 unless max_retries.positive?
|
|
24
|
+
#
|
|
25
|
+
# # good
|
|
26
|
+
# max_retries = Legion::Settings[:llm][:max_retries]
|
|
27
|
+
class NoInlineSettingDefaults < Base
|
|
28
|
+
MSG_OR = 'Inline default `%<default>s` after `Legion::Settings` read. ' \
|
|
29
|
+
'Move the default into `settings.rb` instead.'
|
|
30
|
+
MSG_SHADOW = 'Shadow-default pattern after `Legion::Settings` read. ' \
|
|
31
|
+
'Move the default into `settings.rb` instead.'
|
|
32
|
+
|
|
33
|
+
def on_or(node)
|
|
34
|
+
left = node.children.first
|
|
35
|
+
right = node.children.last
|
|
36
|
+
return unless settings_read?(left)
|
|
37
|
+
return unless right && literal?(right)
|
|
38
|
+
|
|
39
|
+
add_offense(node, message: format(MSG_OR, default: right.source))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def on_if(node)
|
|
43
|
+
cond = node.condition
|
|
44
|
+
return unless cond&.send_type?
|
|
45
|
+
|
|
46
|
+
receiver = cond.receiver
|
|
47
|
+
return unless receiver&.lvar_type?
|
|
48
|
+
|
|
49
|
+
method_name = cond.method_name
|
|
50
|
+
return unless %i[positive? nil? zero? empty? blank?].include?(method_name)
|
|
51
|
+
|
|
52
|
+
var_name = receiver.children.first
|
|
53
|
+
return unless preceded_by_settings_read?(node, var_name)
|
|
54
|
+
|
|
55
|
+
add_offense(node, message: MSG_SHADOW)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def settings_read?(node)
|
|
61
|
+
return false unless node&.send_type?
|
|
62
|
+
return true if direct_settings_access?(node)
|
|
63
|
+
return true if nested_settings_access?(node)
|
|
64
|
+
|
|
65
|
+
false
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def direct_settings_access?(node)
|
|
69
|
+
node.method_name == :[] &&
|
|
70
|
+
node.receiver&.const_type? &&
|
|
71
|
+
node.receiver.children.last == :Settings &&
|
|
72
|
+
legion_const?(node.receiver.children.first)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def nested_settings_access?(node)
|
|
76
|
+
node.method_name == :[] &&
|
|
77
|
+
node.receiver&.send_type? &&
|
|
78
|
+
node.receiver.method_name == :[] &&
|
|
79
|
+
direct_settings_access?(node.receiver)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def legion_const?(node)
|
|
83
|
+
return true if node.nil?
|
|
84
|
+
return false unless node.const_type?
|
|
85
|
+
|
|
86
|
+
node.children.last == :Legion
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def literal?(node)
|
|
90
|
+
%i[str int float sym].include?(node.type)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def preceded_by_settings_read?(if_node, var_name)
|
|
94
|
+
parent = if_node.parent
|
|
95
|
+
return false unless parent&.begin_type?
|
|
96
|
+
|
|
97
|
+
idx = parent.children.index(if_node)
|
|
98
|
+
return false unless idx&.positive?
|
|
99
|
+
|
|
100
|
+
prev = parent.children[idx - 1]
|
|
101
|
+
return false unless prev&.lvasgn_type?
|
|
102
|
+
return false unless prev.children.first == var_name
|
|
103
|
+
|
|
104
|
+
settings_read?(prev.children.last)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Framework
|
|
7
|
+
# Flags `respond_to?` checks for canonical response shape methods
|
|
8
|
+
# (`thinking`, `tool_calls`, `content`, `stop_reason`) and string-key
|
|
9
|
+
# fallback access (`x[:k] || x['k']`). Per R10, downstream consumers
|
|
10
|
+
# of canonical types should rely on the struct interface, not duck-type.
|
|
11
|
+
#
|
|
12
|
+
# Ships **disabled by default**. Enable per-repo or per-directory once
|
|
13
|
+
# the canonical migration is complete.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# # bad
|
|
17
|
+
# response.respond_to?(:thinking)
|
|
18
|
+
# response.respond_to?(:tool_calls)
|
|
19
|
+
# tc[:name] || tc['name']
|
|
20
|
+
#
|
|
21
|
+
# # good
|
|
22
|
+
# response.thinking
|
|
23
|
+
# response.tool_calls
|
|
24
|
+
# tc[:name]
|
|
25
|
+
class NoShapeDuckTyping < Base
|
|
26
|
+
MSG_RESPOND = 'Use canonical struct access instead of `respond_to?(:%<method>s)`. ' \
|
|
27
|
+
'Downstream of translators, the shape is guaranteed.'
|
|
28
|
+
MSG_FALLBACK = 'String-key fallback `%<key>s` suggests duck-typed access. ' \
|
|
29
|
+
'Use canonical struct access or symbol keys consistently.'
|
|
30
|
+
|
|
31
|
+
CANONICAL_METHODS = %w[thinking tool_calls content stop_reason].freeze
|
|
32
|
+
|
|
33
|
+
def on_send(node)
|
|
34
|
+
check_respond_to(node)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def on_or(node)
|
|
38
|
+
check_string_key_fallback(node)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def check_respond_to(node)
|
|
44
|
+
return unless node.method_name == :respond_to?
|
|
45
|
+
return unless node.arguments.length == 1
|
|
46
|
+
|
|
47
|
+
arg = node.arguments.first
|
|
48
|
+
method_name = case arg.type
|
|
49
|
+
when :sym then arg.value.to_s
|
|
50
|
+
when :str then arg.value
|
|
51
|
+
else return
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
return unless CANONICAL_METHODS.include?(method_name)
|
|
55
|
+
|
|
56
|
+
add_offense(node, message: format(MSG_RESPOND, method: method_name))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def check_string_key_fallback(node)
|
|
60
|
+
left = node.children.first
|
|
61
|
+
right = node.children.last
|
|
62
|
+
return unless left&.send_type? && right&.send_type?
|
|
63
|
+
return unless left.method_name == :[] && right.method_name == :[]
|
|
64
|
+
|
|
65
|
+
# Both must access the same receiver
|
|
66
|
+
left_recv = left.receiver
|
|
67
|
+
right_recv = right.receiver
|
|
68
|
+
return unless left_recv && right_recv
|
|
69
|
+
return unless same_receiver?(left_recv, right_recv)
|
|
70
|
+
|
|
71
|
+
# One must be sym, the other str
|
|
72
|
+
left_key = left.arguments.first
|
|
73
|
+
right_key = right.arguments.first
|
|
74
|
+
return unless left_key && right_key
|
|
75
|
+
|
|
76
|
+
has_sym = left_key.sym_type? || right_key.sym_type?
|
|
77
|
+
has_str = left_key.str_type? || right_key.str_type?
|
|
78
|
+
return unless has_sym && has_str
|
|
79
|
+
|
|
80
|
+
# Extract the key name from the symbol side
|
|
81
|
+
key_node = left_key.sym_type? ? left_key : right_key
|
|
82
|
+
return unless key_node
|
|
83
|
+
|
|
84
|
+
add_offense(node, message: format(MSG_FALLBACK, key: key_node.value))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def same_receiver?(left, right)
|
|
88
|
+
# Both lvars with same name
|
|
89
|
+
return true if left.lvar_type? && right.lvar_type? && left.children.first == right.children.first
|
|
90
|
+
|
|
91
|
+
# Both sends with same method name and no receiver (bare identifier)
|
|
92
|
+
left.send_type? && right.send_type? &&
|
|
93
|
+
left.receiver.nil? && right.receiver.nil? &&
|
|
94
|
+
left.method_name == right.method_name
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Framework
|
|
7
|
+
# Bans underscore-prefixed keyword arguments and `**_rest`/`**_` splat
|
|
8
|
+
# parameters in method definitions. The N×N routing design requires
|
|
9
|
+
# explicit kwarg signatures: required → plain kwarg, optional → defaulted
|
|
10
|
+
# kwarg, passthrough → `**opts` at the end.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad
|
|
14
|
+
# def foo(_bar:); end
|
|
15
|
+
# def foo(**_rest); end
|
|
16
|
+
# def foo(**_); end
|
|
17
|
+
#
|
|
18
|
+
# # good
|
|
19
|
+
# def foo(bar:); end
|
|
20
|
+
# def foo(bar: nil); end
|
|
21
|
+
# def foo(**opts); end
|
|
22
|
+
class NoUnderscorePrefixedKwargs < Base
|
|
23
|
+
extend AutoCorrector
|
|
24
|
+
|
|
25
|
+
MSG_KWARG = 'Underscore-prefixed kwarg `%<name>s` is not allowed. ' \
|
|
26
|
+
'Use plain kwarg (required), defaulted kwarg (optional), ' \
|
|
27
|
+
'or `**opts` for passthrough.'
|
|
28
|
+
MSG_SPLAT = 'Underscore-prefixed kwarg splat `%<name>s` is not allowed. ' \
|
|
29
|
+
'Use `**opts` for passthrough.'
|
|
30
|
+
|
|
31
|
+
def on_def(node)
|
|
32
|
+
check_params(node)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def on_defs(node)
|
|
36
|
+
check_params(node)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def check_params(method_node)
|
|
42
|
+
args = method_node.arguments
|
|
43
|
+
return unless args
|
|
44
|
+
|
|
45
|
+
args.each_child_node do |arg|
|
|
46
|
+
case arg.type
|
|
47
|
+
when :kwarg, :kwoptarg
|
|
48
|
+
check_kwarg(arg)
|
|
49
|
+
when :kwrestarg
|
|
50
|
+
check_kwrestarg(arg)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def check_kwarg(arg)
|
|
56
|
+
name = arg.children.first
|
|
57
|
+
return unless name.is_a?(Symbol)
|
|
58
|
+
return unless name.to_s.start_with?('_')
|
|
59
|
+
|
|
60
|
+
add_offense(arg, message: format(MSG_KWARG, name: name)) do |corrector|
|
|
61
|
+
corrected = name.to_s.sub(/\A_+/, '')
|
|
62
|
+
corrected = 'arg' if corrected.empty?
|
|
63
|
+
corrector.replace(arg.source_range, kwarg_replacement(corrected, arg))
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def check_kwrestarg(arg)
|
|
68
|
+
name = arg.children.first
|
|
69
|
+
return unless name.is_a?(Symbol)
|
|
70
|
+
return unless name.to_s.start_with?('_')
|
|
71
|
+
|
|
72
|
+
add_offense(arg, message: format(MSG_SPLAT, name: name)) do |corrector|
|
|
73
|
+
corrector.replace(arg.source_range, '**opts')
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def kwarg_replacement(name, arg)
|
|
78
|
+
if arg.kwoptarg_type?
|
|
79
|
+
default = arg.children.last
|
|
80
|
+
"#{name}: #{default.source}"
|
|
81
|
+
else
|
|
82
|
+
"#{name}:"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Llm
|
|
7
|
+
# Flags `loop do` in legion-llm production code. Unbounded loops have caused
|
|
8
|
+
# runaway threads and hard-to-debug hangs. Use `N.times`, `each`, or a
|
|
9
|
+
# `while` with an explicit counter instead.
|
|
10
|
+
#
|
|
11
|
+
# No auto-correct is provided — the fix requires choosing the correct bound.
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# # bad
|
|
15
|
+
# loop do
|
|
16
|
+
# attempt = dispatch_request
|
|
17
|
+
# break if attempt.success?
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# # good
|
|
21
|
+
# MAX_ATTEMPTS.times do
|
|
22
|
+
# attempt = dispatch_request
|
|
23
|
+
# break if attempt.success?
|
|
24
|
+
# end
|
|
25
|
+
class NoLoopDo < Base
|
|
26
|
+
MSG = '`loop do` is prohibited — use bounded iteration (`N.times`, `each`, ' \
|
|
27
|
+
'or `while` with an explicit decrement) instead.'
|
|
28
|
+
|
|
29
|
+
def on_block(node)
|
|
30
|
+
return unless node.send_node.method_name == :loop
|
|
31
|
+
return unless node.send_node.receiver.nil?
|
|
32
|
+
|
|
33
|
+
add_offense(node.send_node)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
alias on_numblock on_block
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Llm
|
|
7
|
+
# Flags `handle_exception(e, level: :debug)` in rescue blocks. Exception
|
|
8
|
+
# handlers must use at least `:warn` so that errors are visible in
|
|
9
|
+
# production logs without enabling debug mode.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# rescue StandardError => e
|
|
14
|
+
# handle_exception(e, level: :debug, operation: 'foo')
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# rescue StandardError => e
|
|
19
|
+
# handle_exception(e, level: :warn, operation: 'foo')
|
|
20
|
+
# end
|
|
21
|
+
class RescueLogLevel < Base
|
|
22
|
+
MSG = '`handle_exception` called with `level: :debug` in a rescue. ' \
|
|
23
|
+
'Minimum log level for exceptions is `:warn`.'
|
|
24
|
+
|
|
25
|
+
DEBUG_LEVELS = %i[debug trace verbose].freeze
|
|
26
|
+
|
|
27
|
+
def on_send(node)
|
|
28
|
+
return unless node.method_name == :handle_exception
|
|
29
|
+
return unless inside_rescue?(node)
|
|
30
|
+
return unless debug_level?(node)
|
|
31
|
+
|
|
32
|
+
add_offense(node)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def debug_level?(node)
|
|
38
|
+
pairs = node.arguments.flat_map do |arg|
|
|
39
|
+
if arg.hash_type?
|
|
40
|
+
arg.children
|
|
41
|
+
elsif arg.pair_type?
|
|
42
|
+
[arg]
|
|
43
|
+
else
|
|
44
|
+
[]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
pairs.any? do |pair|
|
|
48
|
+
key, val = pair.children
|
|
49
|
+
key.sym_type? && key.value == :level &&
|
|
50
|
+
val.sym_type? && DEBUG_LEVELS.include?(val.value)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def inside_rescue?(node)
|
|
55
|
+
node.each_ancestor(:resbody).any?
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Llm
|
|
7
|
+
# Flags direct write-path access to `Legion::Settings.loader.settings[...][...]`
|
|
8
|
+
# outside test files. Production code must use `Legion::Settings.dig(...)` so all
|
|
9
|
+
# reads go through the typed, validated accessor.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# Legion::Settings.loader.settings[:extensions][:llm][:default_model] = 'gemma-12b'
|
|
14
|
+
# Legion::Settings.loader.settings[:llm][:timeout]
|
|
15
|
+
#
|
|
16
|
+
# # good
|
|
17
|
+
# Legion::Settings.dig(:extensions, :llm, :default_model)
|
|
18
|
+
class SettingsAccessPath < Base
|
|
19
|
+
MSG = 'Use `Legion::Settings.dig(...)` instead of ' \
|
|
20
|
+
'`Legion::Settings.loader.settings[...][...]`.'
|
|
21
|
+
|
|
22
|
+
def on_send(node)
|
|
23
|
+
return unless loader_settings_chain?(node)
|
|
24
|
+
|
|
25
|
+
add_offense(node)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def loader_settings_chain?(node)
|
|
31
|
+
return false unless %i[[] []=].include?(node.method_name)
|
|
32
|
+
|
|
33
|
+
# For []=, the second index arg and the value are children[1] and [2]
|
|
34
|
+
# The receiver of the outer []= is the inner [] node
|
|
35
|
+
receiver = node.receiver
|
|
36
|
+
return false unless receiver&.send_type? && receiver.method_name == :[]
|
|
37
|
+
|
|
38
|
+
# receiver.receiver should be Legion::Settings.loader.settings
|
|
39
|
+
inner = receiver.receiver
|
|
40
|
+
return false unless inner&.send_type? && inner.method_name == :settings
|
|
41
|
+
|
|
42
|
+
loader_recv = inner.receiver
|
|
43
|
+
return false unless loader_recv&.send_type? && loader_recv.method_name == :loader
|
|
44
|
+
|
|
45
|
+
settings_const?(loader_recv.receiver)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def settings_const?(node)
|
|
49
|
+
return false unless node&.const_type?
|
|
50
|
+
return false unless node.children.last == :Settings
|
|
51
|
+
|
|
52
|
+
parent = node.children.first
|
|
53
|
+
return true if parent.nil?
|
|
54
|
+
return false unless parent.const_type? && parent.children.last == :Legion
|
|
55
|
+
|
|
56
|
+
parent.children.first.nil?
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Llm
|
|
7
|
+
# Validates lane field literals against the LegionIO taxonomy enums.
|
|
8
|
+
# Flags `:tier`, `:type`, and `:circuit_state` literals that are not in
|
|
9
|
+
# the known-valid sets, catching typos at lint time.
|
|
10
|
+
#
|
|
11
|
+
# Valid values (from Inventory::DEFAULT_PROVIDER_TIERS and the SSOT design):
|
|
12
|
+
# tier: :local, :fleet, :cloud, :frontier, :direct
|
|
13
|
+
# type: :inference, :embedding, :moderation, :rerank, :image
|
|
14
|
+
# circuit_state: :closed, :open, :half_open
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# # bad (typo)
|
|
18
|
+
# { tier: :fronteir }
|
|
19
|
+
# health[:circuit_state] = :half_opened
|
|
20
|
+
#
|
|
21
|
+
# # good
|
|
22
|
+
# { tier: :frontier }
|
|
23
|
+
# health[:circuit_state] = :half_open
|
|
24
|
+
class TaxonomyEnum < Base
|
|
25
|
+
VALID_TIERS = %i[local fleet cloud frontier direct].freeze
|
|
26
|
+
VALID_TYPES = %i[inference embedding moderation rerank image].freeze
|
|
27
|
+
VALID_CIRCUIT_STATES = %i[closed open half_open].freeze
|
|
28
|
+
|
|
29
|
+
MSG_TIER = 'Unknown tier `%<val>s`. Valid: %<valid>s.'
|
|
30
|
+
MSG_TYPE = 'Unknown type `%<val>s`. Valid: %<valid>s.'
|
|
31
|
+
MSG_CIRCUIT = 'Unknown circuit_state `%<val>s`. Valid: %<valid>s.'
|
|
32
|
+
|
|
33
|
+
def on_pair(node)
|
|
34
|
+
key, value = node.children
|
|
35
|
+
return unless key.sym_type? && value.sym_type?
|
|
36
|
+
|
|
37
|
+
check_taxonomy(key.value, value, node)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def on_send(node)
|
|
41
|
+
# health[:circuit_state] = :bad_value
|
|
42
|
+
return unless node.method_name == :[]=
|
|
43
|
+
return unless node.arguments.size == 2
|
|
44
|
+
|
|
45
|
+
key_node, val_node = node.arguments
|
|
46
|
+
return unless key_node.sym_type? && val_node.sym_type?
|
|
47
|
+
|
|
48
|
+
check_taxonomy(key_node.value, val_node, node)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def check_taxonomy(key, val_node, parent_node)
|
|
54
|
+
val = val_node.value
|
|
55
|
+
case key
|
|
56
|
+
when :tier
|
|
57
|
+
unless VALID_TIERS.include?(val)
|
|
58
|
+
add_offense(parent_node,
|
|
59
|
+
message: format(MSG_TIER, val: val, valid: VALID_TIERS.join(', ')))
|
|
60
|
+
end
|
|
61
|
+
when :type
|
|
62
|
+
unless VALID_TYPES.include?(val)
|
|
63
|
+
add_offense(parent_node,
|
|
64
|
+
message: format(MSG_TYPE, val: val, valid: VALID_TYPES.join(', ')))
|
|
65
|
+
end
|
|
66
|
+
when :circuit_state
|
|
67
|
+
unless VALID_CIRCUIT_STATES.include?(val)
|
|
68
|
+
msg = format(MSG_CIRCUIT, val: val, valid: VALID_CIRCUIT_STATES.join(', '))
|
|
69
|
+
add_offense(parent_node, message: msg)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/rubocop-legion.rb
CHANGED
|
@@ -44,6 +44,10 @@ require 'rubocop/cop/legion/framework/module_function_private'
|
|
|
44
44
|
require 'rubocop/cop/legion/framework/cache_time_coercion'
|
|
45
45
|
require 'rubocop/cop/legion/framework/api_string_keys'
|
|
46
46
|
require 'rubocop/cop/legion/framework/mutex_nested_sync'
|
|
47
|
+
require 'rubocop/cop/legion/framework/no_underscore_prefixed_kwargs'
|
|
48
|
+
require 'rubocop/cop/legion/framework/no_inline_setting_defaults'
|
|
49
|
+
require 'rubocop/cop/legion/framework/no_direct_dispatch'
|
|
50
|
+
require 'rubocop/cop/legion/framework/no_shape_duck_typing'
|
|
47
51
|
|
|
48
52
|
# Legion/Extension
|
|
49
53
|
require 'rubocop/cop/legion/extension/actor_singular_module'
|
|
@@ -64,3 +68,9 @@ require 'rubocop/cop/legion/extension/absorber_missing_pattern'
|
|
|
64
68
|
require 'rubocop/cop/legion/extension/absorber_missing_absorb_method'
|
|
65
69
|
require 'rubocop/cop/legion/extension/definition_call_mismatched'
|
|
66
70
|
require 'rubocop/cop/legion/extension/actor_enabled_side_effects'
|
|
71
|
+
|
|
72
|
+
# Legion/Llm
|
|
73
|
+
require 'rubocop/cop/legion/llm/settings_access_path'
|
|
74
|
+
require 'rubocop/cop/legion/llm/no_loop_do'
|
|
75
|
+
require 'rubocop/cop/legion/llm/rescue_log_level'
|
|
76
|
+
require 'rubocop/cop/legion/llm/taxonomy_enum'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-legion
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -144,6 +144,10 @@ files:
|
|
|
144
144
|
- lib/rubocop/cop/legion/framework/faraday_xml_middleware.rb
|
|
145
145
|
- lib/rubocop/cop/legion/framework/module_function_private.rb
|
|
146
146
|
- lib/rubocop/cop/legion/framework/mutex_nested_sync.rb
|
|
147
|
+
- lib/rubocop/cop/legion/framework/no_direct_dispatch.rb
|
|
148
|
+
- lib/rubocop/cop/legion/framework/no_inline_setting_defaults.rb
|
|
149
|
+
- lib/rubocop/cop/legion/framework/no_shape_duck_typing.rb
|
|
150
|
+
- lib/rubocop/cop/legion/framework/no_underscore_prefixed_kwargs.rb
|
|
147
151
|
- lib/rubocop/cop/legion/framework/sinatra_host_auth.rb
|
|
148
152
|
- lib/rubocop/cop/legion/framework/thor_reserved_run.rb
|
|
149
153
|
- lib/rubocop/cop/legion/helper_migration/defined_transport_guard.rb
|
|
@@ -159,6 +163,10 @@ files:
|
|
|
159
163
|
- lib/rubocop/cop/legion/helper_migration/logging_guard.rb
|
|
160
164
|
- lib/rubocop/cop/legion/helper_migration/old_logging_methods.rb
|
|
161
165
|
- lib/rubocop/cop/legion/helper_migration/require_defined_guard.rb
|
|
166
|
+
- lib/rubocop/cop/legion/llm/no_loop_do.rb
|
|
167
|
+
- lib/rubocop/cop/legion/llm/rescue_log_level.rb
|
|
168
|
+
- lib/rubocop/cop/legion/llm/settings_access_path.rb
|
|
169
|
+
- lib/rubocop/cop/legion/llm/taxonomy_enum.rb
|
|
162
170
|
- lib/rubocop/cop/legion/rescue_logging/bare_rescue.rb
|
|
163
171
|
- lib/rubocop/cop/legion/rescue_logging/no_capture.rb
|
|
164
172
|
- lib/rubocop/cop/legion/rescue_logging/silent_capture.rb
|
|
@@ -191,7 +199,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
191
199
|
- !ruby/object:Gem::Version
|
|
192
200
|
version: '0'
|
|
193
201
|
requirements: []
|
|
194
|
-
rubygems_version:
|
|
202
|
+
rubygems_version: 3.6.9
|
|
195
203
|
specification_version: 4
|
|
196
204
|
summary: LegionIO code quality cops for RuboCop
|
|
197
205
|
test_files: []
|