rubocop-claude 0.1.0
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 +7 -0
- data/CHANGELOG.md +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +267 -0
- data/config/default.yml +202 -0
- data/exe/rubocop-claude +7 -0
- data/lib/rubocop/cop/claude/explicit_visibility.rb +139 -0
- data/lib/rubocop/cop/claude/mystery_regex.rb +46 -0
- data/lib/rubocop/cop/claude/no_backwards_compat_hacks.rb +140 -0
- data/lib/rubocop/cop/claude/no_commented_code.rb +182 -0
- data/lib/rubocop/cop/claude/no_fancy_unicode.rb +173 -0
- data/lib/rubocop/cop/claude/no_hardcoded_line_numbers.rb +142 -0
- data/lib/rubocop/cop/claude/no_overly_defensive_code.rb +160 -0
- data/lib/rubocop/cop/claude/tagged_comments.rb +78 -0
- data/lib/rubocop-claude.rb +19 -0
- data/lib/rubocop_claude/cli.rb +246 -0
- data/lib/rubocop_claude/generator.rb +90 -0
- data/lib/rubocop_claude/init_wizard/hooks_installer.rb +127 -0
- data/lib/rubocop_claude/init_wizard/linter_configurer.rb +88 -0
- data/lib/rubocop_claude/init_wizard/preferences_gatherer.rb +94 -0
- data/lib/rubocop_claude/plugin.rb +34 -0
- data/lib/rubocop_claude/version.rb +5 -0
- data/rubocop-claude.gemspec +41 -0
- data/templates/cops/class-structure.md +58 -0
- data/templates/cops/disable-cops-directive.md +33 -0
- data/templates/cops/explicit-visibility.md +52 -0
- data/templates/cops/metrics.md +73 -0
- data/templates/cops/mystery-regex.md +54 -0
- data/templates/cops/no-backwards-compat-hacks.md +101 -0
- data/templates/cops/no-commented-code.md +74 -0
- data/templates/cops/no-fancy-unicode.md +72 -0
- data/templates/cops/no-hardcoded-line-numbers.md +70 -0
- data/templates/cops/no-overly-defensive-code.md +117 -0
- data/templates/cops/tagged-comments.md +74 -0
- data/templates/hooks/settings.local.json +15 -0
- data/templates/linting.md +81 -0
- metadata +183 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Claude/NoBackwardsCompatHacks
|
|
2
|
+
|
|
3
|
+
**What it catches:** Self-documented compatibility hacks - code with comments like "for backwards compatibility" or markers like `# removed:`.
|
|
4
|
+
|
|
5
|
+
**Why it matters:** Dead code should be deleted, not preserved "helpfully."
|
|
6
|
+
|
|
7
|
+
## The Principle
|
|
8
|
+
|
|
9
|
+
When removing or replacing code, **delete it completely**. Don't:
|
|
10
|
+
- Leave commented tombstones (`# removed: old_method`)
|
|
11
|
+
- Create "compatibility shims" (`OldName = NewName`)
|
|
12
|
+
- Silence linters with underscore prefixes (`_unused = old_value`)
|
|
13
|
+
- Add wrapper methods that just delegate to new ones
|
|
14
|
+
- Keep empty methods "for backwards compatibility"
|
|
15
|
+
|
|
16
|
+
Version control preserves history. Callers should be updated, not shimmed.
|
|
17
|
+
|
|
18
|
+
## What This Cop Detects
|
|
19
|
+
|
|
20
|
+
This cop catches **self-documented** hacks - patterns where you've helpfully commented your intent:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# CAUGHT - has compat comment
|
|
24
|
+
OldName = NewName # for backwards compatibility
|
|
25
|
+
|
|
26
|
+
# CAUGHT - dead code marker
|
|
27
|
+
# removed: def old_method; end
|
|
28
|
+
# deprecated: use new_method instead
|
|
29
|
+
|
|
30
|
+
# NOT CAUGHT - no comment (silent hack)
|
|
31
|
+
OldName = NewName
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Detection Philosophy
|
|
35
|
+
|
|
36
|
+
We only catch ~30% of compat hacks (the self-documented ones). This is intentional.
|
|
37
|
+
|
|
38
|
+
**The goal is teaching, not comprehensive detection.**
|
|
39
|
+
|
|
40
|
+
When this cop fires, you're reading this guidance and learning the principle. Even if we can't catch every case, the teaching moments shape better habits over time.
|
|
41
|
+
|
|
42
|
+
## How to Fix
|
|
43
|
+
|
|
44
|
+
### Dead Code Markers
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
# BAD
|
|
48
|
+
# removed: def old_method; end
|
|
49
|
+
# deprecated: use new_method instead
|
|
50
|
+
# legacy: keeping for backwards compat
|
|
51
|
+
|
|
52
|
+
# GOOD - just delete the comment entirely
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Constant Re-exports
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
# BAD
|
|
59
|
+
OldClassName = NewClassName # for backwards compatibility
|
|
60
|
+
|
|
61
|
+
# GOOD - delete the alias, update all callers
|
|
62
|
+
# (yes, even if there are many callers)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Underscore Assignments (optional check)
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
# BAD (if CheckUnderscoreAssignments: true)
|
|
69
|
+
_old_value = calculate_something
|
|
70
|
+
|
|
71
|
+
# GOOD - delete the line if the value isn't needed
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## For AI Assistants
|
|
75
|
+
|
|
76
|
+
**When you're about to add a compat shim, stop.** Ask yourself:
|
|
77
|
+
|
|
78
|
+
1. "Am I preserving dead code?" → Delete it instead
|
|
79
|
+
2. "Will callers break?" → Update the callers
|
|
80
|
+
3. "There are too many callers" → Update them anyway, or ask the human
|
|
81
|
+
4. "What if someone needs the old behavior?" → They can use git history
|
|
82
|
+
|
|
83
|
+
**Never:**
|
|
84
|
+
- Add `# for backwards compatibility` comments
|
|
85
|
+
- Create `OldName = NewName` aliases
|
|
86
|
+
- Use `_unused = value` to silence linters
|
|
87
|
+
- Leave `# removed:` or `# deprecated:` markers
|
|
88
|
+
- Create wrapper methods that just delegate
|
|
89
|
+
|
|
90
|
+
**Instead:**
|
|
91
|
+
- Delete the old code completely
|
|
92
|
+
- Update all callers to use the new code
|
|
93
|
+
- If unsure, ask: "Should I update all callers or is there a reason to keep the old interface?"
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
```yaml
|
|
98
|
+
Claude/NoBackwardsCompatHacks:
|
|
99
|
+
Enabled: true
|
|
100
|
+
CheckUnderscoreAssignments: false # Optional, off by default
|
|
101
|
+
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Claude/NoCommentedCode
|
|
2
|
+
|
|
3
|
+
**What it catches:** Commented-out code (even single lines by default).
|
|
4
|
+
|
|
5
|
+
**Why it matters:** Commented code is technical debt. Version control preserves history - just delete it.
|
|
6
|
+
|
|
7
|
+
**Autocorrectable:** Yes (unsafe) - deletes the commented code. Run `rubocop -A` to auto-fix.
|
|
8
|
+
|
|
9
|
+
## How to Fix
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
# BAD
|
|
13
|
+
# def old_method
|
|
14
|
+
# do_something
|
|
15
|
+
# end
|
|
16
|
+
|
|
17
|
+
# BAD - even single lines
|
|
18
|
+
# user.update!(name: "test")
|
|
19
|
+
|
|
20
|
+
# GOOD - just delete the commented code entirely
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## The KEEP Exception
|
|
24
|
+
|
|
25
|
+
Sometimes you genuinely need to preserve commented code temporarily. Use a `KEEP` comment with attribution:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
# GOOD - explicit, attributed, time-boxed
|
|
29
|
+
# KEEP [@username]: Rollback path during migration, remove after 2025-06
|
|
30
|
+
# def legacy_method
|
|
31
|
+
# old_implementation
|
|
32
|
+
# end
|
|
33
|
+
|
|
34
|
+
# BAD - KEEP without attribution doesn't work
|
|
35
|
+
# KEEP: I might need this later
|
|
36
|
+
# def old_method
|
|
37
|
+
# do_something
|
|
38
|
+
# end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### KEEP Rules
|
|
42
|
+
|
|
43
|
+
1. **Must have attribution** - `[@handle]` format (same as TaggedComments)
|
|
44
|
+
2. **Must have justification** - explain why it's kept
|
|
45
|
+
3. **Should be time-boxed** - include a removal date when possible
|
|
46
|
+
4. **Only protects the immediately following block** - prose comments break the protection
|
|
47
|
+
|
|
48
|
+
## For AI Assistants
|
|
49
|
+
|
|
50
|
+
**Default behavior: delete commented code.** Don't ask "should I keep this?" - the answer is delete.
|
|
51
|
+
|
|
52
|
+
If you encounter a `# KEEP` comment with valid attribution, leave it alone. The human made an explicit decision to preserve that code.
|
|
53
|
+
|
|
54
|
+
**Never add KEEP comments yourself** unless the human explicitly asks you to preserve specific code temporarily.
|
|
55
|
+
|
|
56
|
+
## What's NOT Commented Code
|
|
57
|
+
|
|
58
|
+
These are fine and won't be flagged:
|
|
59
|
+
|
|
60
|
+
- Explanatory prose comments
|
|
61
|
+
- Documentation (YARD/RDoc)
|
|
62
|
+
- TODO/FIXME/NOTE annotations
|
|
63
|
+
- `@example` blocks in documentation
|
|
64
|
+
- RuboCop directives
|
|
65
|
+
|
|
66
|
+
## Configuration
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
Claude/NoCommentedCode:
|
|
70
|
+
MinLines: 1 # Flag even single lines (default)
|
|
71
|
+
AllowKeep: true # Honor KEEP comments with attribution (default)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Set `MinLines: 2` if single-line detection is too noisy for your codebase.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Claude/NoFancyUnicode
|
|
2
|
+
|
|
3
|
+
**What it catches:** Non-standard Unicode characters outside the allowed set (letters, numbers, ASCII symbols).
|
|
4
|
+
|
|
5
|
+
**Why it matters:** Fancy Unicode causes subtle bugs. Curly quotes `""` break string matching. Mathematical symbols `≠` look like `!=` but aren't. Em-dashes `—` aren't hyphens. Emoji reduce professionalism.
|
|
6
|
+
|
|
7
|
+
## How to Fix
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# BAD - curly quotes
|
|
11
|
+
puts "Hello world"
|
|
12
|
+
|
|
13
|
+
# BAD - em-dash
|
|
14
|
+
# Section 3 — Details
|
|
15
|
+
|
|
16
|
+
# BAD - mathematical symbol
|
|
17
|
+
puts "x ≠ y"
|
|
18
|
+
|
|
19
|
+
# BAD - emoji
|
|
20
|
+
puts "Success! 🎉"
|
|
21
|
+
status = :done_✅
|
|
22
|
+
|
|
23
|
+
# GOOD - ASCII equivalents
|
|
24
|
+
puts "Hello world"
|
|
25
|
+
|
|
26
|
+
# GOOD - double hyphen or just hyphen
|
|
27
|
+
# Section 3 -- Details
|
|
28
|
+
|
|
29
|
+
# GOOD - ASCII operators
|
|
30
|
+
puts "x != y"
|
|
31
|
+
|
|
32
|
+
# GOOD - no emoji
|
|
33
|
+
puts "Success!"
|
|
34
|
+
status = :done
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Allowed Characters
|
|
38
|
+
|
|
39
|
+
- **Letters** - Any script: Latin, Chinese, Japanese, Cyrillic, Arabic, etc.
|
|
40
|
+
- **Numbers** - Any script
|
|
41
|
+
- **Combining marks** - Accents, diacritics (café, José)
|
|
42
|
+
- **ASCII printable** - All standard keyboard symbols (0x20-0x7E)
|
|
43
|
+
- **Whitespace** - Tabs, newlines
|
|
44
|
+
|
|
45
|
+
## Configuration Options
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
Claude/NoFancyUnicode:
|
|
49
|
+
AllowedUnicode: ['→', '←', '•'] # Specific chars to permit
|
|
50
|
+
AllowInStrings: false # Skip checking strings
|
|
51
|
+
AllowInComments: false # Skip checking comments
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Common Replacements
|
|
55
|
+
|
|
56
|
+
| Fancy | ASCII | Description |
|
|
57
|
+
|-------|-------|-------------|
|
|
58
|
+
| `"` `"` | `"` | Curly quotes to straight quotes |
|
|
59
|
+
| `'` `'` | `'` | Curly apostrophes to straight |
|
|
60
|
+
| `—` | `--` | Em-dash to double hyphen |
|
|
61
|
+
| `–` | `-` | En-dash to hyphen |
|
|
62
|
+
| `≠` | `!=` | Not equal |
|
|
63
|
+
| `≤` `≥` | `<=` `>=` | Comparison operators |
|
|
64
|
+
| `→` `←` | `->` `<-` | Arrows |
|
|
65
|
+
| `•` | `*` or `-` | Bullet |
|
|
66
|
+
|
|
67
|
+
## When to Allow
|
|
68
|
+
|
|
69
|
+
Add to `AllowedUnicode` if the character is:
|
|
70
|
+
- Required by external API or data format
|
|
71
|
+
- Part of user-facing content where typography matters
|
|
72
|
+
- In comments explaining Unicode behavior
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Claude/NoHardcodedLineNumbers
|
|
2
|
+
|
|
3
|
+
**What it catches:** Hardcoded line numbers in comments and strings that become stale when code shifts.
|
|
4
|
+
|
|
5
|
+
**Why it matters:** References like "see line 42" or "foo.rb:123" break silently as code evolves. Use stable references like method names, class names, or descriptive comments instead.
|
|
6
|
+
|
|
7
|
+
## How to Fix
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# BAD - line numbers shift when code changes
|
|
11
|
+
# see line 42 for details
|
|
12
|
+
# Error defined at foo.rb:123
|
|
13
|
+
raise "error at line 42"
|
|
14
|
+
|
|
15
|
+
# GOOD - use stable references
|
|
16
|
+
# see #validate_input for details
|
|
17
|
+
# Error defined in FooError class
|
|
18
|
+
raise "error in validate_input"
|
|
19
|
+
|
|
20
|
+
# GOOD - use descriptive comments
|
|
21
|
+
# The validation logic below handles edge cases
|
|
22
|
+
# See the ErrorHandler module for error definitions
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Patterns Detected
|
|
26
|
+
|
|
27
|
+
| Pattern | Example | Description |
|
|
28
|
+
|---------|---------|-------------|
|
|
29
|
+
| `line N` | `# see line 42` | Natural language reference |
|
|
30
|
+
| `lines N` | `# lines 42-50` | Plural form |
|
|
31
|
+
| `LN` | `# see L42` | GitHub-style reference |
|
|
32
|
+
| `.rb:N` | `# foo.rb:123` | Ruby file:line format |
|
|
33
|
+
| `.erb:N` | `# app.erb:10` | ERB file:line format |
|
|
34
|
+
| `.rake:N` | `# tasks.rake:5` | Rake file:line format |
|
|
35
|
+
|
|
36
|
+
## Patterns NOT Flagged
|
|
37
|
+
|
|
38
|
+
These are explicitly ignored:
|
|
39
|
+
|
|
40
|
+
- Version strings: `Ruby 3.1`, `v1.2.3`, `1.2.3`
|
|
41
|
+
- Port numbers: `port 8080`
|
|
42
|
+
- IDs: `id: 42`, `pid: 1234`
|
|
43
|
+
- Time durations: `30 seconds`, `100ms`
|
|
44
|
+
- Byte sizes: `100 bytes`, `5mb`
|
|
45
|
+
- Percentages: `50%`
|
|
46
|
+
- Dollar amounts: `$42`
|
|
47
|
+
- Issue references: `#42`
|
|
48
|
+
|
|
49
|
+
## Configuration
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
Claude/NoHardcodedLineNumbers:
|
|
53
|
+
Enabled: true
|
|
54
|
+
CheckComments: true # Check comments for line refs (default: true)
|
|
55
|
+
CheckStrings: true # Check string literals (default: true)
|
|
56
|
+
MinLineNumber: 1 # Only flag line numbers >= this (default: 1)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Better Alternatives
|
|
60
|
+
|
|
61
|
+
| Instead of... | Use... |
|
|
62
|
+
|---------------|--------|
|
|
63
|
+
| `# see line 42` | `# see #method_name` |
|
|
64
|
+
| `# foo.rb:123` | `# see FooClass#method` |
|
|
65
|
+
| `"error at line 42"` | `"error in validate_input"` |
|
|
66
|
+
| `# L42 handles this` | `# The validation block handles this` |
|
|
67
|
+
|
|
68
|
+
## Edge Cases
|
|
69
|
+
|
|
70
|
+
The cop reports only the first line number per node to avoid noisy output. Heredocs are intentionally ignored since they often contain documentation or templates where line references may be intentional.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Claude/NoOverlyDefensiveCode
|
|
2
|
+
|
|
3
|
+
**What it catches:**
|
|
4
|
+
1. `rescue => e; nil` or `rescue nil` - swallowing errors
|
|
5
|
+
2. 2+ chained `&.` operators - excessive safe navigation
|
|
6
|
+
3. `a && a.foo` - defensive nil check before method call
|
|
7
|
+
4. `a.present? && a.foo` - defensive presence check
|
|
8
|
+
5. `foo.nil? ? default : foo` - verbose nil ternary
|
|
9
|
+
6. `foo ? foo : default` - verbose identity ternary
|
|
10
|
+
|
|
11
|
+
**Why it matters:** Defensive code hides bugs and indicates distrust of the codebase. Internal code should be trusted; errors should propagate.
|
|
12
|
+
|
|
13
|
+
## The Principle
|
|
14
|
+
|
|
15
|
+
Don't code defensively against your own codebase. When you add defensive patterns, you're saying "I don't trust this code." Either:
|
|
16
|
+
1. Fix the code so it's trustworthy, or
|
|
17
|
+
2. Handle the error/nil explicitly and meaningfully
|
|
18
|
+
|
|
19
|
+
## Error Swallowing
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
# BAD - swallows all errors
|
|
23
|
+
begin
|
|
24
|
+
risky_operation
|
|
25
|
+
rescue => e
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# BAD - inline form
|
|
30
|
+
result = dangerous_call rescue nil
|
|
31
|
+
|
|
32
|
+
# GOOD - let errors propagate
|
|
33
|
+
result = risky_operation
|
|
34
|
+
|
|
35
|
+
# GOOD - specific exceptions with intentional ignore
|
|
36
|
+
begin
|
|
37
|
+
require 'optional_gem'
|
|
38
|
+
rescue LoadError
|
|
39
|
+
# Optional dependency not available
|
|
40
|
+
end
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Excessive Safe Navigation
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# BAD - 2+ chained &. violates design principles
|
|
47
|
+
user&.profile&.settings
|
|
48
|
+
|
|
49
|
+
# GOOD - single &. at system boundary
|
|
50
|
+
user&.name
|
|
51
|
+
|
|
52
|
+
# GOOD - trust your data model
|
|
53
|
+
user.profile.settings.notifications
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Defensive Nil Checks
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
# BAD - pre-safe-navigation pattern
|
|
60
|
+
a && a.foo
|
|
61
|
+
user && user.name
|
|
62
|
+
|
|
63
|
+
# BAD - presence check before method call
|
|
64
|
+
user.present? && user.name
|
|
65
|
+
|
|
66
|
+
# GOOD (default: AddSafeNavigator: false) - trust the code
|
|
67
|
+
a.foo
|
|
68
|
+
user.name
|
|
69
|
+
|
|
70
|
+
# ALTERNATIVE (AddSafeNavigator: true) - if you really need nil safety
|
|
71
|
+
a&.foo
|
|
72
|
+
user&.name
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Verbose Ternaries
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
# BAD - verbose nil check
|
|
79
|
+
foo.nil? ? default : foo
|
|
80
|
+
value.blank? ? fallback : value
|
|
81
|
+
|
|
82
|
+
# BAD - verbose identity check
|
|
83
|
+
foo ? foo : default
|
|
84
|
+
|
|
85
|
+
# GOOD - use ||
|
|
86
|
+
foo || default
|
|
87
|
+
value || fallback
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## For AI Assistants
|
|
91
|
+
|
|
92
|
+
**When you're about to add defensive code, stop.** Ask yourself:
|
|
93
|
+
|
|
94
|
+
1. "Why don't I trust this code?" → Fix the trust issue instead
|
|
95
|
+
2. "What error am I hiding?" → Let it propagate or handle it properly
|
|
96
|
+
3. "Why might this be nil?" → Fix the data model or handle at the boundary
|
|
97
|
+
|
|
98
|
+
**The right response to uncertainty is not defensive code.** It's:
|
|
99
|
+
- Understanding why the uncertainty exists
|
|
100
|
+
- Fixing the root cause
|
|
101
|
+
- Or asking the human: "This could be nil/error here - how should I handle it?"
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
Claude/NoOverlyDefensiveCode:
|
|
107
|
+
Enabled: true
|
|
108
|
+
MaxSafeNavigationChain: 1 # Flag 2+ chained &. operators
|
|
109
|
+
AddSafeNavigator: false # Autocorrect `a && a.foo` to `a.foo` (fail fast)
|
|
110
|
+
# Set to true for `a&.foo` (add safe nav)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Related Cops
|
|
114
|
+
|
|
115
|
+
If using `rubocop-rails`, also enable:
|
|
116
|
+
- `Rails/Present` - catches `a && a.present?`
|
|
117
|
+
- `Rails/Blank` - catches `a.blank? && a.foo`
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Claude/TaggedComments
|
|
2
|
+
|
|
3
|
+
**What it catches:** TODO/FIXME/NOTE/HACK comments without attribution.
|
|
4
|
+
|
|
5
|
+
**Why it matters:** Anonymous TODOs lose context. Attribution tracks ownership and distinguishes human comments from AI-generated ones.
|
|
6
|
+
|
|
7
|
+
## How to Fix
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# BAD
|
|
11
|
+
# TODO: Refactor this method
|
|
12
|
+
# FIXME: Handle edge case
|
|
13
|
+
|
|
14
|
+
# GOOD - human attribution
|
|
15
|
+
# TODO [@username]: Refactor this method - it's doing too much
|
|
16
|
+
# FIXME [Alice - @alice]: Handle edge case where user is nil
|
|
17
|
+
|
|
18
|
+
# GOOD - AI attribution
|
|
19
|
+
# TODO [@claude]: Consider extracting to a service object
|
|
20
|
+
# NOTE [@claude]: This mirrors the pattern in user_factory.rb
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## For AI Assistants
|
|
24
|
+
|
|
25
|
+
**Your handle is `@claude`.** When you write TODO/FIXME/NOTE/HACK comments, always use `[@claude]`:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
# TODO [@claude]: This method could be simplified
|
|
29
|
+
# NOTE [@claude]: Intentionally duplicated from BaseController for isolation
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This makes it easy to find AI-generated comments later with `grep -r "@claude"`.
|
|
33
|
+
|
|
34
|
+
## Attribution Format
|
|
35
|
+
|
|
36
|
+
Required format: `[@handle]` or `[Name - @handle]`
|
|
37
|
+
|
|
38
|
+
| Format | Example | Valid |
|
|
39
|
+
|--------|---------|-------|
|
|
40
|
+
| Handle only | `[@username]` | Yes |
|
|
41
|
+
| Name + handle | `[Alice - @alice]` | Yes |
|
|
42
|
+
| Full name + handle | `[Alice Smith - @alice]` | Yes |
|
|
43
|
+
| No @ symbol | `[username]` | No |
|
|
44
|
+
| No handle | `[Alice]` | No |
|
|
45
|
+
| Plain text | `[some text]` | No |
|
|
46
|
+
|
|
47
|
+
## Placement
|
|
48
|
+
|
|
49
|
+
Attribution can appear anywhere in the comment:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
# Both valid:
|
|
53
|
+
# TODO [@username]: Fix this later
|
|
54
|
+
# TODO: Fix this later [@username]
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Configuration Options
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
Claude/TaggedComments:
|
|
61
|
+
Keywords:
|
|
62
|
+
- TODO
|
|
63
|
+
- FIXME
|
|
64
|
+
- NOTE # Default keywords
|
|
65
|
+
- HACK
|
|
66
|
+
- OPTIMIZE
|
|
67
|
+
- REVIEW
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## When Fixing Existing Comments
|
|
71
|
+
|
|
72
|
+
- **Your own new comments:** Use `[@claude]`
|
|
73
|
+
- **Existing anonymous comments:** Ask the human whose attribution to use
|
|
74
|
+
- **Don't guess:** If unsure, ask "Who should I attribute this TODO to?"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Linting
|
|
2
|
+
|
|
3
|
+
Run `bin/standardrb` (or `bin/rubocop`) before committing. Fix all errors.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| Cop | Fix |
|
|
8
|
+
|-----|-----|
|
|
9
|
+
| **Claude/NoFancyUnicode** | Remove emoji and fancy Unicode. Use ASCII text. |
|
|
10
|
+
| **Claude/TaggedComments** | Add attribution: `# TODO: [@handle] description` |
|
|
11
|
+
| **Claude/NoCommentedCode** | Delete commented-out code. Use version control. |
|
|
12
|
+
| **Claude/NoBackwardsCompatHacks** | Delete dead code. Don't preserve for compatibility. |
|
|
13
|
+
| **Claude/NoOverlyDefensiveCode** | Trust internal code. Remove `rescue nil` and excessive `&.` chains. |
|
|
14
|
+
| **Claude/ExplicitVisibility** | Use consistent visibility style (grouped or modifier). |
|
|
15
|
+
| **Claude/MysteryRegex** | Extract long regexes to named constants. |
|
|
16
|
+
|
|
17
|
+
## When to Ask
|
|
18
|
+
|
|
19
|
+
- If a cop seems wrong for this codebase, ask before disabling
|
|
20
|
+
- If you're unsure how to fix, ask rather than guessing
|
|
21
|
+
- Never add `# rubocop:disable` without discussing first
|
|
22
|
+
|
|
23
|
+
## Common Patterns
|
|
24
|
+
|
|
25
|
+
### Tagged Comments
|
|
26
|
+
```ruby
|
|
27
|
+
# bad
|
|
28
|
+
# TODO fix this later
|
|
29
|
+
|
|
30
|
+
# good
|
|
31
|
+
# TODO: [@username] fix this later
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Commented Code
|
|
35
|
+
```ruby
|
|
36
|
+
# bad - delete this, don't comment it out
|
|
37
|
+
# def old_method
|
|
38
|
+
# do_something
|
|
39
|
+
# end
|
|
40
|
+
|
|
41
|
+
# good - just delete it, git has history
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Defensive Code
|
|
45
|
+
```ruby
|
|
46
|
+
# bad - swallowing errors
|
|
47
|
+
result = dangerous_call rescue nil
|
|
48
|
+
|
|
49
|
+
# bad - excessive safe navigation
|
|
50
|
+
user&.profile&.settings&.value
|
|
51
|
+
|
|
52
|
+
# bad - defensive nil check
|
|
53
|
+
user && user.name
|
|
54
|
+
|
|
55
|
+
# good - trust internal code
|
|
56
|
+
result = dangerous_call
|
|
57
|
+
user.profile.settings.value
|
|
58
|
+
user.name
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Visibility Style
|
|
62
|
+
|
|
63
|
+
Check `.rubocop.yml` for `EnforcedStyle` (grouped or modifier):
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
# grouped style (default)
|
|
67
|
+
class Foo
|
|
68
|
+
def public_method; end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def private_method; end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# modifier style
|
|
76
|
+
class Foo
|
|
77
|
+
def public_method; end
|
|
78
|
+
|
|
79
|
+
private def private_method; end
|
|
80
|
+
end
|
|
81
|
+
```
|