yaic 0.1.0 → 0.2.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 +4 -4
- data/.claude/agents/ralph-qa.md +101 -0
- data/.claude/ralph/bin/dump-pid.sh +3 -0
- data/.claude/ralph/bin/kill-claude +6 -0
- data/.claude/ralph/bin/start-ralph +44 -0
- data/.claude/ralph/bin/stop-hook.sh +9 -0
- data/.claude/ralph/bin/stop-ralph +17 -0
- data/.claude/ralph/prompt.md +218 -0
- data/.claude/settings.json +26 -0
- data/.gitmodules +3 -0
- data/CLAUDE.md +65 -0
- data/README.md +106 -17
- data/Rakefile +8 -0
- data/devenv.nix +1 -0
- data/docs/agents/data-model.md +150 -0
- data/docs/agents/ralph/features/01-message-parsing.md.done +160 -0
- data/docs/agents/ralph/features/01-tcpsocket-refactor.md.done +109 -0
- data/docs/agents/ralph/features/02-connection-socket.md.done +138 -0
- data/docs/agents/ralph/features/02-simplified-client-api.md.done +306 -0
- data/docs/agents/ralph/features/03-registration.md.done +147 -0
- data/docs/agents/ralph/features/04-ping-pong.md.done +109 -0
- data/docs/agents/ralph/features/05-event-system.md.done +167 -0
- data/docs/agents/ralph/features/06-privmsg-notice.md.done +163 -0
- data/docs/agents/ralph/features/07-join-part.md.done +190 -0
- data/docs/agents/ralph/features/08-quit.md.done +118 -0
- data/docs/agents/ralph/features/09-nick-change.md.done +109 -0
- data/docs/agents/ralph/features/10-topic.md.done +145 -0
- data/docs/agents/ralph/features/11-kick.md.done +122 -0
- data/docs/agents/ralph/features/12-names.md.done +124 -0
- data/docs/agents/ralph/features/13-mode.md.done +174 -0
- data/docs/agents/ralph/features/14-who-whois.md.done +188 -0
- data/docs/agents/ralph/features/15-client-api.md.done +180 -0
- data/docs/agents/ralph/features/16-ssl-test-infrastructure.md.done +50 -0
- data/docs/agents/ralph/features/17-github-actions-ci.md.done +70 -0
- data/docs/agents/ralph/features/18-brakeman-security-scanning.md.done +67 -0
- data/docs/agents/ralph/features/19-fix-qa.md.done +73 -0
- data/docs/agents/ralph/features/20-test-optimization.md.done +70 -0
- data/docs/agents/ralph/features/21-test-parallelization.md.done +56 -0
- data/docs/agents/ralph/features/22-wait-until-pattern.md.done +90 -0
- data/docs/agents/ralph/features/23-ping-test-optimization.md.done +46 -0
- data/docs/agents/ralph/features/24-blocking-who-whois.md.done +159 -0
- data/docs/agents/ralph/features/25-verbose-mode.md.done +166 -0
- data/docs/agents/ralph/plans/test-optimization-plan.md +172 -0
- data/docs/agents/ralph/progress.md +731 -0
- data/docs/agents/todo.md +5 -0
- data/lib/yaic/channel.rb +22 -0
- data/lib/yaic/client.rb +821 -0
- data/lib/yaic/event.rb +35 -0
- data/lib/yaic/message.rb +119 -0
- data/lib/yaic/registration.rb +17 -0
- data/lib/yaic/socket.rb +120 -0
- data/lib/yaic/source.rb +39 -0
- data/lib/yaic/version.rb +1 -1
- data/lib/yaic/who_result.rb +17 -0
- data/lib/yaic/whois_result.rb +20 -0
- data/lib/yaic.rb +13 -1
- metadata +51 -1
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# WHO and WHOIS
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Implement user information queries. WHO lists users matching criteria, WHOIS provides detailed info about a specific user.
|
|
6
|
+
|
|
7
|
+
## Behavior
|
|
8
|
+
|
|
9
|
+
### WHO Command
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
client.who("#ruby") # List users in channel
|
|
13
|
+
client.who("nick") # Get info on specific user
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Format: `WHO <mask>`
|
|
17
|
+
|
|
18
|
+
### WHO Response
|
|
19
|
+
|
|
20
|
+
- 352 RPL_WHOREPLY - One per matching user
|
|
21
|
+
- 315 RPL_ENDOFWHO - End of list
|
|
22
|
+
|
|
23
|
+
RPL_WHOREPLY format:
|
|
24
|
+
`:server 352 mynick #chan ~user host server nick H :0 realname`
|
|
25
|
+
|
|
26
|
+
Fields:
|
|
27
|
+
- channel or `*`
|
|
28
|
+
- username
|
|
29
|
+
- host
|
|
30
|
+
- server
|
|
31
|
+
- nick
|
|
32
|
+
- flags: H (here) or G (gone/away), optionally `*` (ircop)
|
|
33
|
+
- hopcount and realname (after colon)
|
|
34
|
+
|
|
35
|
+
### WHOIS Command
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
client.whois("dan")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Format: `WHOIS <nick>`
|
|
42
|
+
|
|
43
|
+
### WHOIS Response
|
|
44
|
+
|
|
45
|
+
Common numerics:
|
|
46
|
+
- 311 RPL_WHOISUSER - `nick user host * :realname`
|
|
47
|
+
- 319 RPL_WHOISCHANNELS - Channel list
|
|
48
|
+
- 312 RPL_WHOISSERVER - Server info
|
|
49
|
+
- 317 RPL_WHOISIDLE - Idle time
|
|
50
|
+
- 330 RPL_WHOISACCOUNT - Account name (if identified)
|
|
51
|
+
- 318 RPL_ENDOFWHOIS - End of WHOIS
|
|
52
|
+
|
|
53
|
+
### WHOIS Errors
|
|
54
|
+
|
|
55
|
+
- 401 ERR_NOSUCHNICK - Nick not found
|
|
56
|
+
- 318 RPL_ENDOFWHOIS - Still sent on error
|
|
57
|
+
|
|
58
|
+
### Events
|
|
59
|
+
|
|
60
|
+
For WHO, can emit individual results or collected batch.
|
|
61
|
+
|
|
62
|
+
For WHOIS, collect all numerics until ENDOFWHOIS, then emit `:whois` event with aggregated data.
|
|
63
|
+
|
|
64
|
+
## Models
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
Yaic::WhoisResult
|
|
68
|
+
- nick: String
|
|
69
|
+
- user: String
|
|
70
|
+
- host: String
|
|
71
|
+
- realname: String
|
|
72
|
+
- channels: Array[String]
|
|
73
|
+
- server: String
|
|
74
|
+
- idle: Integer (seconds)
|
|
75
|
+
- signon: Time
|
|
76
|
+
- account: String or nil
|
|
77
|
+
- away: String or nil
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Tests
|
|
81
|
+
|
|
82
|
+
### Integration Tests - WHO
|
|
83
|
+
|
|
84
|
+
**WHO channel**
|
|
85
|
+
- Given: Client in #test with users
|
|
86
|
+
- When: `client.who("#test")`
|
|
87
|
+
- Then: Receive RPL_WHOREPLY for each user, then RPL_ENDOFWHO
|
|
88
|
+
|
|
89
|
+
**WHO specific nick**
|
|
90
|
+
- Given: "target" is online
|
|
91
|
+
- When: `client.who("target")`
|
|
92
|
+
- Then: Receive single RPL_WHOREPLY
|
|
93
|
+
|
|
94
|
+
**WHO non-existent**
|
|
95
|
+
- Given: No such channel/user
|
|
96
|
+
- When: `client.who("nobody")`
|
|
97
|
+
- Then: Receive only RPL_ENDOFWHO (empty results)
|
|
98
|
+
|
|
99
|
+
### Integration Tests - WHOIS
|
|
100
|
+
|
|
101
|
+
**WHOIS user**
|
|
102
|
+
- Given: "target" is online
|
|
103
|
+
- When: `client.whois("target")`
|
|
104
|
+
- Then: Receive RPL_WHOISUSER, RPL_WHOISSERVER, RPL_ENDOFWHOIS
|
|
105
|
+
|
|
106
|
+
**WHOIS with channels**
|
|
107
|
+
- Given: "target" is in channels
|
|
108
|
+
- When: `client.whois("target")`
|
|
109
|
+
- Then: RPL_WHOISCHANNELS shows their channels
|
|
110
|
+
|
|
111
|
+
**WHOIS non-existent**
|
|
112
|
+
- Given: No such nick
|
|
113
|
+
- When: `client.whois("nobody")`
|
|
114
|
+
- Then: Receive 401 ERR_NOSUCHNICK, then RPL_ENDOFWHOIS
|
|
115
|
+
|
|
116
|
+
**WHOIS away user**
|
|
117
|
+
- Given: "target" is away
|
|
118
|
+
- When: `client.whois("target")`
|
|
119
|
+
- Then: Receive 301 RPL_AWAY with away message
|
|
120
|
+
|
|
121
|
+
### Unit Tests - WHO
|
|
122
|
+
|
|
123
|
+
**Parse RPL_WHOREPLY**
|
|
124
|
+
- Given: `:server 352 me #chan ~user host srv nick H :0 Real Name`
|
|
125
|
+
- When: Parse
|
|
126
|
+
- Then: channel="#chan", user="~user", nick="nick", realname="Real Name", away=false
|
|
127
|
+
|
|
128
|
+
**Parse RPL_WHOREPLY away**
|
|
129
|
+
- Given: `:server 352 me #chan ~user host srv nick G :0 Name`
|
|
130
|
+
- When: Parse
|
|
131
|
+
- Then: away=true (G flag)
|
|
132
|
+
|
|
133
|
+
**Format WHO**
|
|
134
|
+
- Given: mask = "#test"
|
|
135
|
+
- When: Build WHO
|
|
136
|
+
- Then: Output = "WHO #test\r\n"
|
|
137
|
+
|
|
138
|
+
### Unit Tests - WHOIS
|
|
139
|
+
|
|
140
|
+
**Parse RPL_WHOISUSER**
|
|
141
|
+
- Given: `:server 311 me nick ~user host * :Real Name`
|
|
142
|
+
- When: Parse
|
|
143
|
+
- Then: nick="nick", user="~user", host="host", realname="Real Name"
|
|
144
|
+
|
|
145
|
+
**Parse RPL_WHOISCHANNELS**
|
|
146
|
+
- Given: `:server 319 me nick :#chan1 @#chan2 +#chan3`
|
|
147
|
+
- When: Parse
|
|
148
|
+
- Then: channels=["#chan1", "#chan2", "#chan3"] with modes noted
|
|
149
|
+
|
|
150
|
+
**Parse RPL_WHOISIDLE**
|
|
151
|
+
- Given: `:server 317 me nick 300 1234567890 :seconds idle`
|
|
152
|
+
- When: Parse
|
|
153
|
+
- Then: idle=300, signon=Time.at(1234567890)
|
|
154
|
+
|
|
155
|
+
**Parse RPL_WHOISACCOUNT**
|
|
156
|
+
- Given: `:server 330 me nick account :is logged in as`
|
|
157
|
+
- When: Parse
|
|
158
|
+
- Then: account="account"
|
|
159
|
+
|
|
160
|
+
**Format WHOIS**
|
|
161
|
+
- Given: nick = "target"
|
|
162
|
+
- When: Build WHOIS
|
|
163
|
+
- Then: Output = "WHOIS target\r\n"
|
|
164
|
+
|
|
165
|
+
### Collection Tests
|
|
166
|
+
|
|
167
|
+
**Collect WHOIS parts**
|
|
168
|
+
- Given: WHOIS in progress
|
|
169
|
+
- When: RPL_WHOISUSER, RPL_WHOISCHANNELS, RPL_WHOISSERVER, RPL_ENDOFWHOIS received
|
|
170
|
+
- Then: Single :whois event with all data
|
|
171
|
+
|
|
172
|
+
**Handle interleaved messages**
|
|
173
|
+
- Given: WHOIS in progress
|
|
174
|
+
- When: Other messages arrive between WHOIS numerics
|
|
175
|
+
- Then: WHOIS data still collected correctly
|
|
176
|
+
|
|
177
|
+
## Implementation Notes
|
|
178
|
+
|
|
179
|
+
- WHOIS numerics may be interleaved with other messages
|
|
180
|
+
- Buffer WHOIS results until ENDOFWHOIS
|
|
181
|
+
- Implement timeout for WHOIS (server may not respond)
|
|
182
|
+
- RPL_WHOISCHANNELS may have mode prefixes (@, +)
|
|
183
|
+
- WHO visibility affected by +i mode
|
|
184
|
+
|
|
185
|
+
## Dependencies
|
|
186
|
+
|
|
187
|
+
- Requires `01-message-parsing.md`
|
|
188
|
+
- Requires `05-event-system.md`
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Client API
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Implement the main `Yaic::Client` class that ties everything together and provides the public interface.
|
|
6
|
+
|
|
7
|
+
## Behavior
|
|
8
|
+
|
|
9
|
+
### Initialization
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
client = Yaic::Client.new(
|
|
13
|
+
server: "irc.libera.chat",
|
|
14
|
+
port: 6697,
|
|
15
|
+
ssl: true,
|
|
16
|
+
nickname: "mynick",
|
|
17
|
+
username: "myuser", # optional, defaults to nickname
|
|
18
|
+
realname: "My Real Name" # optional, defaults to nickname
|
|
19
|
+
)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Connection
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
client.connect # Blocking - connects and starts event loop
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Event Registration
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
client.on(:message) { |event| puts event.text }
|
|
32
|
+
client.on(:join) { |event| puts "#{event.user} joined #{event.channel}" }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Commands
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
client.join("#ruby")
|
|
39
|
+
client.join("#ruby", "key")
|
|
40
|
+
client.part("#ruby")
|
|
41
|
+
client.part("#ruby", "reason")
|
|
42
|
+
client.privmsg("#ruby", "Hello!")
|
|
43
|
+
client.privmsg("nick", "Private message")
|
|
44
|
+
client.notice("#ruby", "Notice")
|
|
45
|
+
client.nick("newnick")
|
|
46
|
+
client.topic("#ruby")
|
|
47
|
+
client.topic("#ruby", "New topic")
|
|
48
|
+
client.quit
|
|
49
|
+
client.quit("Goodbye")
|
|
50
|
+
client.kick("#ruby", "nick")
|
|
51
|
+
client.kick("#ruby", "nick", "reason")
|
|
52
|
+
client.mode("#ruby")
|
|
53
|
+
client.mode("#ruby", "+m")
|
|
54
|
+
client.who("#ruby")
|
|
55
|
+
client.whois("nick")
|
|
56
|
+
client.names("#ruby")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### State Access
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
client.nick # Current nickname
|
|
63
|
+
client.connected? # Boolean
|
|
64
|
+
client.channels # Hash of joined channels
|
|
65
|
+
client.server # Server hostname
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Error Handling
|
|
69
|
+
|
|
70
|
+
- Connection errors raise appropriate exceptions
|
|
71
|
+
- Server errors emit `:error` events
|
|
72
|
+
|
|
73
|
+
## Models
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
Yaic::Client
|
|
77
|
+
- config: Hash
|
|
78
|
+
- socket: Yaic::Socket
|
|
79
|
+
- handlers: Hash[Symbol, Array[Block]]
|
|
80
|
+
- channels: Hash[String, Yaic::Channel]
|
|
81
|
+
- nick: String
|
|
82
|
+
- state: Symbol
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Tests
|
|
86
|
+
|
|
87
|
+
### Integration Tests - Full Flow
|
|
88
|
+
|
|
89
|
+
**Connect and receive welcome**
|
|
90
|
+
- Given: New client configured for inspircd
|
|
91
|
+
- When: `client.connect`
|
|
92
|
+
- Then: Connected, :connect event fired
|
|
93
|
+
|
|
94
|
+
**Join channel and send message**
|
|
95
|
+
- Given: Connected client
|
|
96
|
+
- When: Join #test, send PRIVMSG
|
|
97
|
+
- Then: Message received by other client in channel
|
|
98
|
+
|
|
99
|
+
**Receive and handle message**
|
|
100
|
+
- Given: Connected client in #test with :message handler
|
|
101
|
+
- When: Other client sends message
|
|
102
|
+
- Then: Handler called with correct event
|
|
103
|
+
|
|
104
|
+
**Full session lifecycle**
|
|
105
|
+
- Given: New client
|
|
106
|
+
- When: Connect, join, chat, part, quit
|
|
107
|
+
- Then: All operations succeed, clean disconnect
|
|
108
|
+
|
|
109
|
+
### Unit Tests - Initialization
|
|
110
|
+
|
|
111
|
+
**Default values**
|
|
112
|
+
- Given: Only server, port, ssl, nickname provided
|
|
113
|
+
- When: Create client
|
|
114
|
+
- Then: username = nickname, realname = nickname
|
|
115
|
+
|
|
116
|
+
**Store configuration**
|
|
117
|
+
- Given: Full config provided
|
|
118
|
+
- When: Create client
|
|
119
|
+
- Then: All values accessible
|
|
120
|
+
|
|
121
|
+
### Unit Tests - State
|
|
122
|
+
|
|
123
|
+
**Initially disconnected**
|
|
124
|
+
- Given: New client
|
|
125
|
+
- Then: `client.connected?` = false, `client.state` = :disconnected
|
|
126
|
+
|
|
127
|
+
**After connect**
|
|
128
|
+
- Given: Client connects successfully
|
|
129
|
+
- Then: `client.connected?` = true, `client.state` = :connected
|
|
130
|
+
|
|
131
|
+
**After quit**
|
|
132
|
+
- Given: Connected client quits
|
|
133
|
+
- Then: `client.connected?` = false
|
|
134
|
+
|
|
135
|
+
**Track nickname**
|
|
136
|
+
- Given: Connected as "oldnick"
|
|
137
|
+
- When: Nick changed to "newnick"
|
|
138
|
+
- Then: `client.nick` = "newnick"
|
|
139
|
+
|
|
140
|
+
**Track channels**
|
|
141
|
+
- Given: Connected client
|
|
142
|
+
- When: Join #test
|
|
143
|
+
- Then: `client.channels["#test"]` exists
|
|
144
|
+
|
|
145
|
+
### Unit Tests - Command Methods
|
|
146
|
+
|
|
147
|
+
**join delegates to socket**
|
|
148
|
+
- Given: Connected client
|
|
149
|
+
- When: `client.join("#test")`
|
|
150
|
+
- Then: "JOIN #test" sent
|
|
151
|
+
|
|
152
|
+
**privmsg delegates to socket**
|
|
153
|
+
- Given: Connected client
|
|
154
|
+
- When: `client.privmsg("#test", "Hello")`
|
|
155
|
+
- Then: "PRIVMSG #test :Hello" sent
|
|
156
|
+
|
|
157
|
+
### Error Handling Tests
|
|
158
|
+
|
|
159
|
+
**Connection refused**
|
|
160
|
+
- Given: No server on port
|
|
161
|
+
- When: `client.connect`
|
|
162
|
+
- Then: Raises connection error
|
|
163
|
+
|
|
164
|
+
**Server error numeric**
|
|
165
|
+
- Given: Connected client with :error handler
|
|
166
|
+
- When: Server sends 433 (nick in use)
|
|
167
|
+
- Then: Handler called with error info
|
|
168
|
+
|
|
169
|
+
## Implementation Notes
|
|
170
|
+
|
|
171
|
+
- `connect` should handle the full registration sequence
|
|
172
|
+
- Consider thread-safety for handlers
|
|
173
|
+
- Event loop reads from socket, parses messages, dispatches events
|
|
174
|
+
- All command methods should validate state (connected?) before sending
|
|
175
|
+
- Consider `connect_async` for non-blocking usage
|
|
176
|
+
|
|
177
|
+
## Dependencies
|
|
178
|
+
|
|
179
|
+
- Requires all previous features
|
|
180
|
+
- This is the final integration feature
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# SSL Test Infrastructure
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Set up proper SSL/TLS testing infrastructure so that SSL-related tests can run reliably. This includes configuring the inspircd Docker container with SSL certificates and ensuring tests can connect over TLS.
|
|
6
|
+
|
|
7
|
+
## Behavior
|
|
8
|
+
|
|
9
|
+
### Container Configuration
|
|
10
|
+
|
|
11
|
+
1. Create or configure inspircd container with SSL enabled on port 6697
|
|
12
|
+
2. Generate self-signed certificates for testing
|
|
13
|
+
3. Mount certificates into the container
|
|
14
|
+
4. Update `bin/start-irc-server` to set up SSL-enabled container
|
|
15
|
+
|
|
16
|
+
### Test Updates
|
|
17
|
+
|
|
18
|
+
1. Remove skip logic from SSL tests in `test/integration/socket_test.rb`
|
|
19
|
+
2. SSL tests should run and pass reliably
|
|
20
|
+
3. Verify both SSL connection and read/write operations work
|
|
21
|
+
|
|
22
|
+
## Tests
|
|
23
|
+
|
|
24
|
+
**SSL connection succeeds**
|
|
25
|
+
- Given: inspircd with SSL on localhost:6697
|
|
26
|
+
- When: Connect with ssl: true, verify_mode: VERIFY_NONE
|
|
27
|
+
- Then: Connection established, state is :connecting
|
|
28
|
+
|
|
29
|
+
**SSL read/write works**
|
|
30
|
+
- Given: SSL connection established
|
|
31
|
+
- When: Send NICK/USER commands
|
|
32
|
+
- Then: Receive server responses
|
|
33
|
+
|
|
34
|
+
**SSL certificate verification**
|
|
35
|
+
- Given: Self-signed cert on server
|
|
36
|
+
- When: Connect with VERIFY_PEER
|
|
37
|
+
- Then: Fails unless cert is trusted
|
|
38
|
+
- When: Connect with VERIFY_NONE
|
|
39
|
+
- Then: Succeeds
|
|
40
|
+
|
|
41
|
+
## Implementation Notes
|
|
42
|
+
|
|
43
|
+
- Use OpenSSL to generate self-signed certs: `openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes`
|
|
44
|
+
- Store certs in `test/fixtures/ssl/` or similar
|
|
45
|
+
- May need custom inspircd.conf to enable SSL module
|
|
46
|
+
- Update `bin/stop-irc-server` if container setup changes
|
|
47
|
+
|
|
48
|
+
## Dependencies
|
|
49
|
+
|
|
50
|
+
- Requires `02-connection-socket.md` to be complete (SSL support in Socket class)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# GitHub Actions CI
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Set up GitHub Actions to run tests and linting on pull requests and pushes to the main branch. Tests run on Ruby 3.2 and 3.4 (bottom and top of current EOL lifecycle). Linting runs as a separate job.
|
|
6
|
+
|
|
7
|
+
## Behavior
|
|
8
|
+
|
|
9
|
+
### Workflow Triggers
|
|
10
|
+
|
|
11
|
+
The workflow runs on:
|
|
12
|
+
- Push to `main` branch
|
|
13
|
+
- All pull requests
|
|
14
|
+
|
|
15
|
+
### Jobs
|
|
16
|
+
|
|
17
|
+
**1. Lint Job**
|
|
18
|
+
- Runs `bundle exec standardrb` (or `bundle exec rake standard`)
|
|
19
|
+
- Single job, not matrixed
|
|
20
|
+
- Fails fast if code doesn't pass linting
|
|
21
|
+
|
|
22
|
+
**2. Test Job**
|
|
23
|
+
- Matrix: Ruby 3.2 and Ruby 3.4
|
|
24
|
+
- Runs `bundle exec rake test`
|
|
25
|
+
- Uses `ruby/setup-ruby@v1` with `bundler-cache: true`
|
|
26
|
+
|
|
27
|
+
### Workflow File
|
|
28
|
+
|
|
29
|
+
Located at `.github/workflows/main.yml` (update existing file).
|
|
30
|
+
|
|
31
|
+
## Implementation Notes
|
|
32
|
+
|
|
33
|
+
- The existing workflow triggers on `master` which doesn't exist - change to `main`
|
|
34
|
+
- Current workflow only tests Ruby 3.4.1 - expand to matrix with 3.2 and 3.4
|
|
35
|
+
- Split lint into its own job so it fails fast and doesn't duplicate across matrix
|
|
36
|
+
- Use `actions/checkout@v4` with `persist-credentials: false`
|
|
37
|
+
|
|
38
|
+
## Tests
|
|
39
|
+
|
|
40
|
+
This feature has no unit tests. Verification is done by:
|
|
41
|
+
|
|
42
|
+
1. Push the workflow changes to a branch
|
|
43
|
+
2. Open a PR or push to main
|
|
44
|
+
3. Verify CI runs successfully using `gh run list` and `gh run view`
|
|
45
|
+
4. All jobs (lint, test on 3.2, test on 3.4) must pass
|
|
46
|
+
|
|
47
|
+
### Verification Commands
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Check recent workflow runs
|
|
51
|
+
gh run list --limit 5
|
|
52
|
+
|
|
53
|
+
# View specific run details
|
|
54
|
+
gh run view <run-id>
|
|
55
|
+
|
|
56
|
+
# Check if latest run on current branch passed
|
|
57
|
+
gh run list --branch $(git branch --show-current) --limit 1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Completion Criteria
|
|
61
|
+
|
|
62
|
+
- [ ] `.github/workflows/main.yml` updated with correct configuration
|
|
63
|
+
- [ ] Workflow triggers on `main` branch (not `master`)
|
|
64
|
+
- [ ] Lint job runs separately
|
|
65
|
+
- [ ] Test matrix includes Ruby 3.2 and 3.4
|
|
66
|
+
- [ ] CI run passes (verified via `gh run list` showing success)
|
|
67
|
+
|
|
68
|
+
## Dependencies
|
|
69
|
+
|
|
70
|
+
None - this is the first feature.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Brakeman Security Scanning
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
Add Brakeman security scanning to CI. Brakeman is a static analysis tool that checks Ruby on Rails applications for security vulnerabilities. This runs as a separate GitHub Actions job.
|
|
6
|
+
|
|
7
|
+
## Behavior
|
|
8
|
+
|
|
9
|
+
### Workflow Integration
|
|
10
|
+
|
|
11
|
+
Add a new job to `.github/workflows/main.yml`:
|
|
12
|
+
|
|
13
|
+
**Security Job**
|
|
14
|
+
- Runs `bin/brakeman` (or `bundle exec brakeman`)
|
|
15
|
+
- Single job, not matrixed (security scanning doesn't need multiple Ruby versions)
|
|
16
|
+
- Can run in parallel with other jobs
|
|
17
|
+
|
|
18
|
+
### Brakeman Setup
|
|
19
|
+
|
|
20
|
+
If `bin/brakeman` doesn't exist, create it as a binstub or run via `bundle exec brakeman`.
|
|
21
|
+
|
|
22
|
+
Add `brakeman` gem to Gemfile if not present (in development/test group).
|
|
23
|
+
|
|
24
|
+
## Implementation Notes
|
|
25
|
+
|
|
26
|
+
- Brakeman may report existing issues in the codebase
|
|
27
|
+
- If Brakeman fails, fix the issues or configure Brakeman to ignore false positives
|
|
28
|
+
- Use `brakeman --no-pager` for CI output
|
|
29
|
+
- Consider `brakeman -o /dev/stdout -o brakeman-output.html` for both console and artifact output
|
|
30
|
+
|
|
31
|
+
### Handling Existing Issues
|
|
32
|
+
|
|
33
|
+
If the codebase has existing Brakeman warnings:
|
|
34
|
+
1. Run `brakeman` locally first
|
|
35
|
+
2. Fix any real security issues
|
|
36
|
+
3. For false positives, use `brakeman -I` to create/update `config/brakeman.ignore`
|
|
37
|
+
4. Commit the ignore file if needed
|
|
38
|
+
|
|
39
|
+
## Tests
|
|
40
|
+
|
|
41
|
+
This feature has no unit tests. Verification is done by:
|
|
42
|
+
|
|
43
|
+
1. Ensure Brakeman passes locally: `bundle exec brakeman`
|
|
44
|
+
2. Push changes and verify CI runs
|
|
45
|
+
3. Security job must pass in GitHub Actions
|
|
46
|
+
|
|
47
|
+
### Verification Commands
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Run Brakeman locally
|
|
51
|
+
bundle exec brakeman
|
|
52
|
+
|
|
53
|
+
# Check CI status
|
|
54
|
+
gh run list --limit 5
|
|
55
|
+
gh run view <run-id>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Completion Criteria
|
|
59
|
+
|
|
60
|
+
- [ ] Brakeman gem added to Gemfile (if not present)
|
|
61
|
+
- [ ] Brakeman runs successfully locally with no errors
|
|
62
|
+
- [ ] Security job added to `.github/workflows/main.yml`
|
|
63
|
+
- [ ] CI run passes including the security job (verified via `gh run list`)
|
|
64
|
+
|
|
65
|
+
## Dependencies
|
|
66
|
+
|
|
67
|
+
- Requires `17-github-actions-ci.md` (workflow file must exist and work)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Look at 02-simplified-client-api.md.done
|
|
2
|
+
An agent flag that feature as done without the QA passing on it. Which is TOTALLY UNACCEPTABLE.
|
|
3
|
+
|
|
4
|
+
Do not rerun QA before you're ready to commit. I'll give you the QA output:
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
QA FAILED. Issues found:
|
|
8
|
+
|
|
9
|
+
1. Public timeout parameters violate feature spec (/Users/joedupuis/workspace/yaic/lib/yaic/client.rb lines
|
|
10
|
+
55, 159, 166, 184):
|
|
11
|
+
- The feature spec at
|
|
12
|
+
/Users/joedupuis/workspace/yaic/docs/agents/ralph/features/02-simplified-client-api.md.done explicitly
|
|
13
|
+
states:
|
|
14
|
+
- Line 32: "All methods block until the operation completes. No bangs, no timeout params, no manual
|
|
15
|
+
socket handling."
|
|
16
|
+
- Lines 86-90: "Timeouts are an internal concern. Use sensible defaults... Users don't need to think
|
|
17
|
+
about this."
|
|
18
|
+
- However, the implementation exposes timeout: keyword arguments in the public API:
|
|
19
|
+
def connect(timeout: DEFAULT_CONNECT_TIMEOUT)
|
|
20
|
+
def join(channel, key = nil, timeout: DEFAULT_OPERATION_TIMEOUT)
|
|
21
|
+
def part(channel, reason = nil, timeout: DEFAULT_OPERATION_TIMEOUT)
|
|
22
|
+
def nick(new_nick = nil, timeout: DEFAULT_OPERATION_TIMEOUT)
|
|
23
|
+
- Fix: Remove the public timeout: parameter from these method signatures. Keep the internal constants and
|
|
24
|
+
use them directly in wait_until.
|
|
25
|
+
2. Integration tests still use instance_variable_get(:@socket) (Multiple files):
|
|
26
|
+
- /Users/joedupuis/workspace/yaic/test/integration/mode_test.rb lines 79, 103, 130
|
|
27
|
+
- /Users/joedupuis/workspace/yaic/test/integration/kick_test.rb lines 17, 43, 88, 110, 146
|
|
28
|
+
- /Users/joedupuis/workspace/yaic/test/integration/names_test.rb lines 34, 63, 91
|
|
29
|
+
- /Users/joedupuis/workspace/yaic/test/integration/nick_test.rb lines 33, 51
|
|
30
|
+
- /Users/joedupuis/workspace/yaic/test/integration/join_part_test.rb lines 53, 108
|
|
31
|
+
- /Users/joedupuis/workspace/yaic/test/integration/who_whois_test.rb line 133
|
|
32
|
+
- The feature spec (lines 136-139) explicitly says to remove:
|
|
33
|
+
- instance_variable_get
|
|
34
|
+
- Manual read loops
|
|
35
|
+
- Custom wait helpers
|
|
36
|
+
- on_socket_connected
|
|
37
|
+
- These tests use instance_variable_get(:@socket) to call become_oper and send raw commands like OPER and
|
|
38
|
+
SAMODE.
|
|
39
|
+
- Fix: Either add a public raw or send_raw method to the Client class for sending raw IRC commands, or
|
|
40
|
+
refactor the tests to not require OPER privileges (if possible). The become_oper helper pattern using direct
|
|
41
|
+
socket access is exactly what the feature was meant to eliminate.
|
|
42
|
+
|
|
43
|
+
Required fixes:
|
|
44
|
+
1. Remove timeout: parameter from the public method signatures of connect, join, part, and nick.
|
|
45
|
+
2. Either add a public raw(command) method to send arbitrary IRC commands, or refactor integration tests
|
|
46
|
+
that need OPER privileges to avoid accessing internal socket.
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
and
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
Two issues found:
|
|
53
|
+
|
|
54
|
+
1. Public timeout parameters violate feature spec - The methods connect, join, part, and nick expose timeout:
|
|
55
|
+
keyword arguments, but the feature spec explicitly states "No bangs, no timeout params" and "Timeouts are an
|
|
56
|
+
internal concern."
|
|
57
|
+
2. Integration tests still use instance_variable_get(:@socket) - Multiple integration tests access the internal
|
|
58
|
+
socket directly via instance_variable_get(:@socket) to call become_oper and send raw commands. The feature spec
|
|
59
|
+
says to remove this pattern.
|
|
60
|
+
|
|
61
|
+
Required fixes:
|
|
62
|
+
1. Remove timeout: parameter from public method signatures
|
|
63
|
+
2. Add a public raw(command) method for sending arbitrary IRC commands, or refactor the tests
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
Fix the mess. If you cant stop and ask me questions.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
I think the timeout is fine. Let's ignore the first part. The second part though with the reaching for instance variable is bad.
|
|
71
|
+
The tests should not have to reach out for internals with instance_variable_get.
|
|
72
|
+
|
|
73
|
+
If you are blocked ask me with the ask question tool.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Test Optimization - Planning
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
The test suite is slow (173 seconds) and needs optimization. This feature involves profiling the tests to identify slow ones, analyzing why they are slow, and creating an optimization plan for user approval.
|
|
6
|
+
|
|
7
|
+
## Behavior
|
|
8
|
+
|
|
9
|
+
### Phase 1: Profiling
|
|
10
|
+
|
|
11
|
+
Run the full test suite with timing enabled using `rake test TESTOPTS="-v"` and capture:
|
|
12
|
+
- Total time for each test file
|
|
13
|
+
- Individual test method times
|
|
14
|
+
- Identify the slowest tests (top 10 or anything over 1 second)
|
|
15
|
+
|
|
16
|
+
### Phase 2: Analysis
|
|
17
|
+
|
|
18
|
+
For each slow test, analyze:
|
|
19
|
+
- Is it an integration test hitting a real IRC server?
|
|
20
|
+
- Does it have unnecessary sleeps or timeouts?
|
|
21
|
+
- Is it doing redundant setup?
|
|
22
|
+
- Could it be parallelized?
|
|
23
|
+
- Is it testing too much in one test?
|
|
24
|
+
|
|
25
|
+
Document findings with specific line numbers and explanations.
|
|
26
|
+
|
|
27
|
+
### Phase 3: Plan Creation
|
|
28
|
+
|
|
29
|
+
Create a plan document at `docs/agents/ralph/plans/test-optimization-plan.md` containing:
|
|
30
|
+
- Summary of profiling results (slowest tests with times)
|
|
31
|
+
- Root causes identified
|
|
32
|
+
- Proposed optimizations with expected impact
|
|
33
|
+
- Risk assessment for each change
|
|
34
|
+
- How test coverage will be preserved
|
|
35
|
+
|
|
36
|
+
### Phase 4: User Approval
|
|
37
|
+
|
|
38
|
+
Use `AskUserQuestion` tool to ask the user to review and approve the plan.
|
|
39
|
+
|
|
40
|
+
If not approved, update the plan based on feedback and ask again.
|
|
41
|
+
|
|
42
|
+
## Tests
|
|
43
|
+
|
|
44
|
+
This feature is meta - it's about planning, not adding new functionality. Success criteria:
|
|
45
|
+
|
|
46
|
+
**Profiling Output**
|
|
47
|
+
- Generate a report showing test execution times
|
|
48
|
+
- Identify tests taking > 500ms
|
|
49
|
+
|
|
50
|
+
**Plan Document**
|
|
51
|
+
- Plan exists at docs/agents/ralph/plans/test-optimization-plan.md
|
|
52
|
+
- Contains profiling results with specific times
|
|
53
|
+
- Contains proposed changes with rationale
|
|
54
|
+
- Contains risk assessment
|
|
55
|
+
|
|
56
|
+
## Implementation Notes
|
|
57
|
+
|
|
58
|
+
- Use `rake test TESTOPTS="-v"` for timing
|
|
59
|
+
- Use `bundle exec standardrb -A` for linting
|
|
60
|
+
|
|
61
|
+
## Dependencies
|
|
62
|
+
|
|
63
|
+
None - this is the first feature.
|
|
64
|
+
|
|
65
|
+
## Next Features
|
|
66
|
+
|
|
67
|
+
After this planning feature is complete, the following implementation features should be created:
|
|
68
|
+
- 21-test-parallelization.md - Enable parallel test execution
|
|
69
|
+
- 22-wait-until-pattern.md - Replace sleep/read_multiple with wait_until
|
|
70
|
+
- 23-ping-test-optimization.md - Optimize the ping test with faster server config
|