pgbus 0.0.1 → 0.1.2

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -3
  3. data/Rakefile +98 -1
  4. data/app/controllers/pgbus/application_controller.rb +8 -0
  5. data/app/controllers/pgbus/recurring_tasks_controller.rb +36 -0
  6. data/app/helpers/pgbus/application_helper.rb +41 -0
  7. data/app/models/pgbus/application_record.rb +7 -0
  8. data/app/models/pgbus/batch_entry.rb +31 -0
  9. data/app/models/pgbus/blocked_execution.rb +40 -0
  10. data/app/models/pgbus/process_entry.rb +9 -0
  11. data/app/models/pgbus/processed_event.rb +9 -0
  12. data/app/models/pgbus/recurring_execution.rb +33 -0
  13. data/app/models/pgbus/recurring_task.rb +42 -0
  14. data/app/models/pgbus/semaphore.rb +29 -0
  15. data/app/views/layouts/pgbus/application.html.erb +1 -0
  16. data/app/views/pgbus/dashboard/_stats_cards.html.erb +9 -1
  17. data/app/views/pgbus/dead_letter/_messages_table.html.erb +55 -18
  18. data/app/views/pgbus/jobs/_enqueued_table.html.erb +46 -8
  19. data/app/views/pgbus/recurring_tasks/_tasks_table.html.erb +79 -0
  20. data/app/views/pgbus/recurring_tasks/index.html.erb +6 -0
  21. data/app/views/pgbus/recurring_tasks/show.html.erb +122 -0
  22. data/config/routes.rb +7 -0
  23. data/lib/active_job/queue_adapters/pgbus_adapter.rb +29 -0
  24. data/lib/generators/pgbus/add_recurring_generator.rb +56 -0
  25. data/lib/generators/pgbus/install_generator.rb +76 -2
  26. data/lib/generators/pgbus/templates/add_recurring_tables.rb.erb +31 -0
  27. data/lib/generators/pgbus/templates/migration.rb.erb +72 -4
  28. data/lib/generators/pgbus/templates/recurring.yml.erb +40 -0
  29. data/lib/generators/pgbus/templates/upgrade_pgmq.rb.erb +30 -0
  30. data/lib/generators/pgbus/upgrade_pgmq_generator.rb +60 -0
  31. data/lib/pgbus/active_job/adapter.rb +3 -6
  32. data/lib/pgbus/active_job/executor.rb +26 -12
  33. data/lib/pgbus/batch.rb +65 -72
  34. data/lib/pgbus/cli.rb +11 -16
  35. data/lib/pgbus/client.rb +32 -15
  36. data/lib/pgbus/concurrency/blocked_execution.rb +32 -37
  37. data/lib/pgbus/concurrency/semaphore.rb +11 -39
  38. data/lib/pgbus/concurrency.rb +10 -2
  39. data/lib/pgbus/configuration.rb +48 -0
  40. data/lib/pgbus/engine.rb +19 -1
  41. data/lib/pgbus/event_bus/handler.rb +10 -23
  42. data/lib/pgbus/instrumentation.rb +29 -0
  43. data/lib/pgbus/pgmq_schema/pgmq_v1.11.0.sql +2123 -0
  44. data/lib/pgbus/pgmq_schema.rb +159 -0
  45. data/lib/pgbus/process/consumer.rb +17 -9
  46. data/lib/pgbus/process/dispatcher.rb +33 -41
  47. data/lib/pgbus/process/heartbeat.rb +15 -23
  48. data/lib/pgbus/process/signal_handler.rb +23 -1
  49. data/lib/pgbus/process/supervisor.rb +79 -2
  50. data/lib/pgbus/process/worker.rb +42 -13
  51. data/lib/pgbus/recurring/already_recorded.rb +7 -0
  52. data/lib/pgbus/recurring/command_job.rb +28 -0
  53. data/lib/pgbus/recurring/config_loader.rb +35 -0
  54. data/lib/pgbus/recurring/schedule.rb +102 -0
  55. data/lib/pgbus/recurring/scheduler.rb +102 -0
  56. data/lib/pgbus/recurring/task.rb +111 -0
  57. data/lib/pgbus/serializer.rb +16 -6
  58. data/lib/pgbus/version.rb +1 -1
  59. data/lib/pgbus/web/data_source.rb +217 -36
  60. data/lib/pgbus.rb +8 -0
  61. data/lib/tasks/pgbus_pgmq.rake +62 -0
  62. metadata +51 -24
  63. data/.bun-version +0 -1
  64. data/.claude/commands/architect.md +0 -100
  65. data/.claude/commands/github-review-comments.md +0 -237
  66. data/.claude/commands/lfg.md +0 -271
  67. data/.claude/commands/review-pr.md +0 -69
  68. data/.claude/commands/security.md +0 -122
  69. data/.claude/commands/tdd.md +0 -148
  70. data/.claude/rules/agents.md +0 -49
  71. data/.claude/rules/coding-style.md +0 -91
  72. data/.claude/rules/git-workflow.md +0 -56
  73. data/.claude/rules/performance.md +0 -73
  74. data/.claude/rules/testing.md +0 -67
  75. data/CLAUDE.md +0 -80
  76. data/CODE_OF_CONDUCT.md +0 -10
  77. data/bun.lock +0 -18
  78. data/docs/README.md +0 -28
  79. data/docs/switch_from_good_job.md +0 -279
  80. data/docs/switch_from_sidekiq.md +0 -226
  81. data/docs/switch_from_solid_queue.md +0 -247
  82. data/package.json +0 -9
  83. data/sig/pgbus.rbs +0 -4
@@ -1,237 +0,0 @@
1
- ---
2
- description: "Use when a PR has unresolved review comments that need responses -- evaluates each comment, implements valid fixes, pushes back on incorrect suggestions, and resolves all threads."
3
- model: claude-opus-4-6
4
- argument-hint: "PR number (e.g., 123 or #123)"
5
- allowed-tools: Bash(gh pr view:*), Bash(gh pr diff:*), Bash(gh pr comment:*), Bash(gh api:*), Bash(git log:*), Bash(git blame:*), Bash(git push:*), Bash(git commit:*), Bash(git add:*), Bash(bundle exec:*), Read, Write, Edit, Glob, Grep, Agent
6
- ---
7
-
8
- # Review GitHub PR Comments: $ARGUMENTS
9
-
10
- You are reviewing and responding to all unresolved review comments on a GitHub pull request. Apply technical rigour -- evaluate each comment against the actual codebase before accepting or rejecting it.
11
-
12
- ## Phase 0: Determine the PR Number
13
-
14
- The user may provide a PR number as `$ARGUMENTS`. Parse it flexibly:
15
-
16
- - `PR123`, `PR 123`, `pr123` -> PR 123
17
- - `123` -> PR 123
18
- - `#123` -> PR 123
19
- - Empty/blank -> auto-detect from current branch
20
-
21
- **If no PR number is provided**, detect it automatically:
22
-
23
- ```bash
24
- gh pr list --author=@me --head="$(git branch --show-current)" --state=open --json number,title
25
- ```
26
-
27
- If exactly one open PR exists for the current branch, use it. If none or multiple, ask the user.
28
-
29
- Once you have the PR number, confirm it:
30
-
31
- ```bash
32
- gh pr view <PR_NUMBER> --json title,state,url
33
- ```
34
-
35
- ---
36
-
37
- ## Phase 1: Fetch All Unresolved Review Comments
38
-
39
- Retrieve all review comments and identify unresolved ones:
40
-
41
- ```bash
42
- # Get all review comments (not resolved)
43
- gh api "repos/mhenrixon/pgbus/pulls/<PR_NUMBER>/comments" --paginate
44
-
45
- # Get all review threads to check resolution status
46
- gh api graphql -f query='
47
- query($owner: String!, $repo: String!, $pr: Int!) {
48
- repository(owner: $owner, name: $repo) {
49
- pullRequest(number: $pr) {
50
- reviewThreads(first: 100) {
51
- nodes {
52
- id
53
- isResolved
54
- path
55
- line
56
- comments(first: 20) {
57
- nodes {
58
- id
59
- databaseId
60
- body
61
- author { login }
62
- createdAt
63
- }
64
- }
65
- }
66
- }
67
- }
68
- }
69
- }
70
- ' -f owner=mhenrixon -f repo=pgbus -F pr=<PR_NUMBER>
71
- ```
72
-
73
- For each unresolved thread, extract:
74
- - Thread ID (for resolving)
75
- - Comment body (the review feedback)
76
- - File path and line number (if inline)
77
- - Author (to understand context)
78
-
79
- Filter to only **unresolved** threads. Skip bot comments (CodeRabbit, dependabot), resolved threads, and PR description comments.
80
-
81
- If there are no unresolved review comments, report that and stop.
82
-
83
- ---
84
-
85
- ## Phase 2: Read and Categorise Each Comment
86
-
87
- For each unresolved comment, read the full body and categorise it:
88
-
89
- | Category | Action |
90
- |----------|--------|
91
- | Valid fix needed | Implement the fix |
92
- | Valid test gap | Add the missing test |
93
- | Valid style/consistency issue | Fix it |
94
- | Incorrect suggestion | Push back with technical reasoning |
95
- | Suggestion conflicts with architecture | Push back, reference existing patterns |
96
- | Over-engineering / YAGNI | Push back, explain why it's unnecessary |
97
- | Unclear | Ask for clarification (do NOT implement) |
98
-
99
- **Before categorising**, always:
100
- 1. Read the actual file and line being commented on
101
- 2. Check if the suggestion is technically correct for THIS codebase
102
- 3. Check if it would break existing functionality
103
- 4. Check if existing patterns/conventions contradict the suggestion
104
- 5. Check CLAUDE.md rules -- project conventions override reviewer preferences
105
-
106
- ---
107
-
108
- ## Phase 3: Implement Accepted Fixes
109
-
110
- For all comments you've decided to accept:
111
-
112
- 1. **Make the code changes** -- edit the relevant files
113
- 2. **Run affected tests** to verify nothing breaks:
114
- ```bash
115
- bundle exec rspec <relevant_spec_files>
116
- ```
117
- 3. **Run validators**:
118
- ```bash
119
- bundle exec rubocop <changed_files>
120
- ```
121
- 4. **Commit** all fixes together with a clear message:
122
- ```bash
123
- git commit -m "$(cat <<'EOF'
124
- fix: address PR review feedback
125
-
126
- - Description of fix 1
127
- - Description of fix 2
128
- EOF
129
- )"
130
- ```
131
- 5. **Push** to the remote branch:
132
- ```bash
133
- git push
134
- ```
135
-
136
- ---
137
-
138
- ## Phase 4: Reply to Every Comment
139
-
140
- For **each** unresolved thread, reply:
141
-
142
- ### For accepted fixes:
143
-
144
- Reply with what was fixed and the commit SHA:
145
-
146
- ```bash
147
- gh api "repos/mhenrixon/pgbus/pulls/<PR>/comments/<COMMENT_ID>/replies" \
148
- --method POST \
149
- -f 'body=Fixed in <SHA>. <Brief description of what changed>.'
150
- ```
151
-
152
- ### For rejected suggestions:
153
-
154
- Reply with technical reasoning:
155
-
156
- ```bash
157
- gh api "repos/mhenrixon/pgbus/pulls/<PR>/comments/<COMMENT_ID>/replies" \
158
- --method POST \
159
- -f 'body=<Technical explanation of why the suggestion was not implemented>'
160
- ```
161
-
162
- ### Resolving threads (via GraphQL):
163
-
164
- After replying, resolve the thread:
165
-
166
- ```bash
167
- gh api graphql -f query='
168
- mutation($threadId: ID!) {
169
- resolveReviewThread(input: {threadId: $threadId}) {
170
- thread { isResolved }
171
- }
172
- }
173
- ' -f threadId=<THREAD_NODE_ID>
174
- ```
175
-
176
- ### For general PR comments (not inline review threads):
177
-
178
- Reply directly:
179
-
180
- ```bash
181
- gh pr comment <PR_NUMBER> --body "<Response addressing each point>"
182
- ```
183
-
184
- ---
185
-
186
- ## Phase 5: Verify Completion
187
-
188
- After processing all comments, verify no unresolved threads remain:
189
-
190
- ```bash
191
- gh api graphql -f query='
192
- query($owner: String!, $repo: String!, $pr: Int!) {
193
- repository(owner: $owner, name: $repo) {
194
- pullRequest(number: $pr) {
195
- reviewThreads(first: 100) {
196
- totalCount
197
- nodes { isResolved }
198
- }
199
- }
200
- }
201
- }
202
- ' -f owner=mhenrixon -f repo=pgbus -F pr=<PR_NUMBER>
203
- ```
204
-
205
- Report the final tally: how many comments were accepted/fixed, how many were pushed back on, and confirm all threads are resolved.
206
-
207
- ---
208
-
209
- ## Response Style
210
-
211
- When replying to comments:
212
-
213
- - **No performative agreement** -- never say "Great point!" or "You're absolutely right!"
214
- - **No gratitude** -- never say "Thanks for catching that"
215
- - **Be direct** -- state the fix or the reasoning, nothing more
216
- - **Reference commits** -- always include the short SHA when a fix was made
217
- - **Be specific** -- when pushing back, reference actual code, not abstract principles
218
-
219
- When pushing back:
220
-
221
- - Use technical reasoning grounded in the actual codebase
222
- - Reference existing patterns if the suggestion contradicts them
223
- - Reference CLAUDE.md rules when applicable
224
- - Explain what would break or what edge case the reviewer missed
225
- - If the suggestion is valid in principle but wrong for this context, say so
226
-
227
- ---
228
-
229
- ## Important Notes
230
-
231
- - Always read the actual code before evaluating a comment -- reviewers sometimes misread diffs
232
- - If a comment reveals a genuine bug you missed, fix it without defensiveness
233
- - If multiple comments suggest the same change, implement it once and reference the fix in all replies
234
- - Bot reviewers (CodeRabbit, etc.) sometimes suggest changes that conflict with project conventions -- verify against CLAUDE.md
235
- - If a new round of review comments appears after your push (from re-review), report that to the user rather than entering an infinite loop
236
-
237
- Now begin by determining the PR number from `$ARGUMENTS` or the current branch.
@@ -1,271 +0,0 @@
1
- ---
2
- description: "Executes full autonomous engineering workflow with verification. Use when implementing complete features, tackling GitHub issues, or running end-to-end development cycles."
3
- model: claude-opus-4-6
4
- argument-hint: "GitHub issue number/URL or feature description"
5
- allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh pr create:*), Bash(gh pr view:*), Bash(bundle exec:*), Bash(git:*), Read, Write, Edit, Glob, Grep, Agent
6
- ---
7
-
8
- # LFG - Full Autonomous Workflow
9
-
10
- Execute a complete engineering workflow with verification at each phase.
11
-
12
- ## Phase 0: Branch Setup
13
-
14
- **BEFORE any other work, prepare the git branch:**
15
-
16
- 1. Check the current branch: `git branch --show-current`
17
- 2. If NOT on `main`, switch: `git checkout main`
18
- 3. Pull latest: `git pull origin main`
19
- 4. Create feature branch: `git checkout -b issue-{number}-{brief-description}` (or `feature/{description}` if no issue number)
20
-
21
- ---
22
-
23
- ## Phase 1: Understand
24
-
25
- ### Step 1: Gather Requirements
26
-
27
- If `$ARGUMENTS` is a GitHub issue number or URL:
28
-
29
- ```bash
30
- gh issue view <number> --json title,body,labels,assignees,comments
31
- ```
32
-
33
- If `$ARGUMENTS` is a description, use it directly.
34
-
35
- ### Step 2: Define Acceptance Criteria
36
-
37
- **MANDATORY:** Write explicit acceptance criteria:
38
-
39
- - **GIVEN** [context/setup]
40
- - **WHEN** [action taken]
41
- - **THEN** [expected outcome]
42
-
43
- You MUST NOT proceed until you can articulate these clearly.
44
-
45
- ### Step 3: Comprehension Gate
46
-
47
- Before proceeding, you must:
48
-
49
- 1. State the problem/feature in one sentence
50
- 2. Explain WHY this is needed (business context)
51
- 3. List what will change from the user's perspective
52
- 4. Identify edge cases not explicitly mentioned
53
- 5. Explain the data flow or code path involved
54
-
55
- If you cannot complete ALL five items, investigate further.
56
-
57
- ### Step 4: Create Task List
58
-
59
- Create a TaskCreate todo list with specific implementation steps.
60
-
61
- ---
62
-
63
- ## Phase 2: Explore
64
-
65
- 1. Find related files (Glob/Grep or Explore agent)
66
- 2. Read existing patterns in similar features
67
- 3. Understand dependencies and integration points
68
- 4. Check existing test coverage
69
- 5. Review PGMQ client interactions in `lib/pgbus/client.rb`
70
- 6. Check ActiveJob adapter in `lib/pgbus/active_job/`
71
- 7. Review event bus patterns in `lib/pgbus/event_bus/`
72
- 8. Check process model in `lib/pgbus/process/`
73
-
74
- ---
75
-
76
- ## Phase 3: Plan
77
-
78
- 1. List files to modify with specific changes
79
- 2. List new files to create with purpose
80
- 3. Identify migration changes needed (PGMQ queues, metadata tables)
81
- 4. Plan test coverage (TDD: tests FIRST)
82
- 5. Update task list with implementation steps
83
- 6. Consider backwards compatibility with existing queue configurations
84
-
85
- ---
86
-
87
- ## Phase 4: Implement (TDD)
88
-
89
- For each logical unit:
90
-
91
- ### 4.1: Write Failing Test First
92
-
93
- Create a test that demonstrates the expected behavior. Run it to confirm it FAILS:
94
-
95
- ```bash
96
- bundle exec rspec <spec_file>
97
- ```
98
-
99
- ### 4.2: Implement Minimum Code
100
-
101
- Write the MINIMUM code to make the test pass. Follow project patterns:
102
-
103
- | Never Do | Always Do |
104
- |----------|-----------|
105
- | Direct PGMQ SQL calls | Use `Pgbus::Client` wrapper |
106
- | Skip queue prefixing | Always use `config.queue_name()` |
107
- | Hardcode queue names | Use configuration |
108
- | Skip visibility timeout | Use PGMQ's native VT mechanism |
109
- | Ignore dead letter routing | Check `read_ct` against `max_retries` |
110
- | Raw SQL in controllers | Use `Web::DataSource` for dashboard |
111
-
112
- ### 4.3: Refactor
113
-
114
- Once green, refactor while keeping tests passing.
115
-
116
- ### 4.4: Validate
117
-
118
- ```bash
119
- bundle exec rubocop <changed_files>
120
- ```
121
-
122
- ### 4.5: Repeat
123
-
124
- Move to next logical unit. Mark task items complete.
125
-
126
- ---
127
-
128
- ## Phase 5: Deep Root Cause Analysis (Bug Fixes Only)
129
-
130
- **If this is a bug fix, apply deep investigation before implementing:**
131
-
132
- ### Trace the Data Lifecycle
133
-
134
- For the message/job/event causing the issue:
135
- - Where and when was the message enqueued? By what adapter call?
136
- - What visibility timeout was set? Has it expired?
137
- - What ASSUMPTIONS does the code make at the failure point?
138
- - Which assumption was violated, and WHY?
139
-
140
- ### Use Git History
141
-
142
- ```bash
143
- git log --oneline -20 <file>
144
- git blame <file>
145
- ```
146
-
147
- - When was the code written? What was the original intent?
148
- - Has something ELSE changed that invalidated the original assumptions?
149
-
150
- ### Map All Callers
151
-
152
- Don't just look at the method that failed:
153
- - Use Grep to find all call sites
154
- - Different contexts (ActiveJob adapter vs event bus vs dashboard)?
155
- - Does the error only happen in ONE context? Why?
156
-
157
- ### Five Whys
158
-
159
- Keep asking WHY until you reach a meaningful fix point:
160
-
161
- 1. Error: X happened -> Why?
162
- 2. Because Y -> Why was Y in that state?
163
- 3. Because Z -> Why wasn't Z prevented?
164
- 4. Because no check existed -> Why not?
165
- 5. **THIS** is where the fix belongs
166
-
167
- ### Fix Location Principle
168
-
169
- The best fix is usually NOT where the error is raised:
170
- - Nil message in executor -> fix in worker that should provide non-nil
171
- - Queue not found -> fix in code that should ensure_queue first
172
- - Race condition -> PGMQ-level visibility timeout
173
- - Dead letter overflow -> fix the retry/DLQ routing logic
174
-
175
- **Ask: "Where is the EARLIEST point I could prevent this error?" Fix there.**
176
-
177
- ### Unacceptable Superficial Fixes -- DO NOT DO THESE
178
-
179
- - `rescue nil` without understanding why the exception occurs
180
- - `&.` to silence nil errors without investigating why nil occurs
181
- - `if object.present?` guards without understanding why missing
182
- - `return if message.nil?` to silently skip processing
183
- - Wrapping everything in `begin/rescue` to swallow errors
184
-
185
- **These HIDE bugs. The root cause continues causing issues elsewhere.**
186
-
187
- ---
188
-
189
- ## Phase 6: Verify
190
-
191
- **ALL of these must pass before committing:**
192
-
193
- ```bash
194
- bundle exec rubocop # Style
195
- bundle exec rspec <relevant_specs> # Tests
196
- ```
197
-
198
- ### Solution Verification
199
-
200
- Re-read the original requirements and verify:
201
- - "If I were the requester, would I consider this fully resolved?"
202
- - "Have I addressed the ROOT CAUSE, not just the symptom?"
203
- - "Do my tests prove the issue is ACTUALLY fixed, not just suppressed?"
204
- - "Does this maintain backwards compatibility?"
205
-
206
- ---
207
-
208
- ## Phase 7: Commit & PR
209
-
210
- ### Commit
211
-
212
- ```bash
213
- git add <specific_files>
214
- git commit -m "$(cat <<'EOF'
215
- feat(scope): brief description
216
-
217
- ## Summary
218
- [What changed and why]
219
-
220
- ## Test Coverage
221
- - spec 1: validates requirement X
222
- - spec 2: validates edge case Y
223
-
224
- ## Verification
225
- - [x] bundle exec rubocop passes
226
- - [x] bundle exec rspec passes
227
- EOF
228
- )"
229
- ```
230
-
231
- ### Push & PR
232
-
233
- ```bash
234
- git push -u origin $(git branch --show-current)
235
-
236
- gh pr create --title "feat(scope): brief description" --body "$(cat <<'EOF'
237
- ## Summary
238
- - Key change 1
239
- - Key change 2
240
-
241
- Closes #<issue_number>
242
-
243
- ## Test plan
244
- - [ ] Scenario 1
245
- - [ ] Scenario 2
246
- EOF
247
- )"
248
- ```
249
-
250
- ---
251
-
252
- ## Verification Checklist
253
-
254
- - [ ] All acceptance criteria met
255
- - [ ] Tests written BEFORE implementation
256
- - [ ] `bundle exec rubocop` passes
257
- - [ ] `bundle exec rspec` passes
258
- - [ ] Backwards compatibility maintained
259
- - [ ] PGMQ operations go through Client wrapper
260
- - [ ] PR created with description
261
-
262
- ---
263
-
264
- ## Handoff
265
-
266
- When complete:
267
- - All phases executed
268
- - Verification passed
269
- - PR created and linked
270
-
271
- Now, execute this workflow for the provided issue or feature.
@@ -1,69 +0,0 @@
1
- ---
2
- description: Review a GitHub pull request for code quality, patterns, and best practices
3
- model: claude-opus-4-6
4
- argument-hint: "PR URL or number (e.g., 5 or https://github.com/mhenrixon/pgbus/pull/5)"
5
- ---
6
-
7
- # PR Review
8
-
9
- Review PR for pattern compliance and issues. Be concise.
10
-
11
- ## Workflow
12
-
13
- 1. Fetch PR details and diff via `mcp__github__pull_request_read`
14
- 2. Categorize files by type
15
- 3. Check for pattern violations
16
- 4. Output structured review
17
-
18
- ## Pattern Violations to Check
19
-
20
- ```ruby
21
- # WRONG -> RIGHT
22
- Direct PGMQ calls outside Client -> Use Pgbus::Client wrapper
23
- Raw SQL in controllers/views -> Use Web::DataSource
24
- Hardcoded queue names -> Use config.queue_name()
25
- Missing visibility timeout -> Always pass vt: parameter
26
- Skip dead letter routing -> Check read_ct > max_retries
27
- No error handling in workers -> Rescue and log via Pgbus.logger
28
- Polling without LISTEN/NOTIFY -> Use enable_notify_insert
29
- Missing queue prefix -> All queues go through config.queue_name
30
- Thread.new for concurrency -> Use Concurrent::* primitives
31
- rescue StandardError => nil -> Specific error handling
32
- ```
33
-
34
- ## Output Format
35
-
36
- ```
37
- ## Files Requiring Manual Review
38
-
39
- | File | Reason |
40
- |------|--------|
41
- | lib/pgbus/process/worker.rb | Worker recycling logic, verify thresholds |
42
- | lib/pgbus/client.rb | PGMQ interaction, check thread safety |
43
-
44
- ## Critical Issues
45
-
46
- - `lib/pgbus/client.rb:45` - Missing mutex synchronization
47
- - `lib/pgbus/process/worker.rb:12` - Memory check not platform-aware
48
-
49
- ## Suggestions (non-blocking)
50
-
51
- - Consider extracting X to shared module
52
-
53
- ## Verdict
54
-
55
- **Request Changes** - Fix thread safety before merge
56
- ```
57
-
58
- ## Tools
59
-
60
- ```
61
- mcp__github__pull_request_read
62
- method: "get" -> PR details
63
- method: "get_diff" -> Changes
64
- method: "get_files" -> File list
65
- method: "get_status" -> CI status
66
-
67
- bundle exec rubocop -> Style checks
68
- bundle exec rspec -> Tests
69
- ```
@@ -1,122 +0,0 @@
1
- ---
2
- description: "Reviews code for security vulnerabilities. Use when auditing PGMQ operations, connection handling, SQL injection risks, or dashboard authentication."
3
- model: claude-opus-4-6
4
- argument-hint: "code, feature, or area to review for security"
5
- ---
6
-
7
- # Security Specialist
8
-
9
- You are the **Security review and vulnerability audit specialist** for pgbus.
10
-
11
- ## Trigger Contexts
12
-
13
- Use this skill when:
14
- - Auditing PGMQ SQL operations for injection risks
15
- - Reviewing connection pool handling
16
- - Checking for race conditions in message processing
17
- - Reviewing deserialization of job arguments / event payloads
18
- - Auditing the dashboard web UI
19
- - Reviewing worker process management
20
-
21
- ## Key Security Concerns for This Gem
22
-
23
- ### SQL Injection via Queue Names
24
-
25
- ```ruby
26
- # BAD: Unsanitized queue name in SQL
27
- connection.execute("SELECT * FROM pgmq.q_#{queue_name}")
28
-
29
- # GOOD: Sanitize queue names
30
- def sanitize_name(name)
31
- name.gsub(/[^a-zA-Z0-9_]/, "")
32
- end
33
- ```
34
-
35
- PGMQ queue names are interpolated into SQL identifiers. The `Client` validates names, but any new code touching queue names must sanitize.
36
-
37
- ### Connection Pool Safety
38
-
39
- - pgmq-ruby uses `connection_pool` gem for thread-safe pooling
40
- - Never hold a connection across async boundaries
41
- - `TransactionalClient` pins a single connection -- ensure it's not leaked
42
- - When using `-> { ActiveRecord::Base.connection.raw_connection }`, connection lifecycle is Rails-managed
43
-
44
- ### Message Deserialization
45
-
46
- ```ruby
47
- # BAD: Unsafe deserialization
48
- Marshal.load(message.message)
49
-
50
- # GOOD: JSON only
51
- JSON.parse(message.message)
52
- ```
53
-
54
- - All payloads are JSONB -- stick to JSON.parse
55
- - ActiveJob's `deserialize` handles GlobalID resolution -- trust it but validate
56
- - Event payloads with `_global_id` call `GlobalID::Locator.locate` -- ensure objects exist
57
-
58
- ### Dashboard Authentication
59
-
60
- - `web_auth` block receives the raw request -- must be configured by the host app
61
- - Default is `nil` (allow all) -- document this clearly
62
- - Dashboard inherits from `ActionController::Base` (isolated from host app)
63
- - Never expose raw PGMQ internals without sanitization
64
- - Filter sensitive job arguments in the UI
65
-
66
- ### Worker Process Security
67
-
68
- - Supervisor forks child processes -- signal handling must be correct
69
- - Worker recycling kills processes -- ensure graceful cleanup
70
- - Heartbeat writes to DB -- validate process ownership
71
- - Memory measurement uses `ps` / `/proc` -- no shell injection risk (pid is numeric)
72
-
73
- ### Visibility Timeout / Message Safety
74
-
75
- - Messages become visible again after VT expires -- idempotency is critical
76
- - `read_ct` tracks redeliveries -- DLQ routing must be reliable
77
- - `FOR UPDATE SKIP LOCKED` prevents double-processing -- verify all read paths use it
78
- - Archive vs delete: archived messages are queryable, deleted are gone
79
-
80
- ## Verification Checklist
81
-
82
- - [ ] No SQL injection via queue names
83
- - [ ] All PGMQ operations go through Client (sanitized)
84
- - [ ] Connection pool properly managed
85
- - [ ] No unsafe deserialization (JSON only)
86
- - [ ] Dashboard auth is configurable and documented
87
- - [ ] Worker signals handled correctly
88
- - [ ] No secrets in logs or error messages
89
- - [ ] Idempotency enforced for event handlers
90
-
91
- ## Security Tools
92
-
93
- ```bash
94
- # Static analysis
95
- bundle exec rubocop
96
-
97
- # Check for known vulnerabilities in dependencies
98
- bundle audit check --update
99
-
100
- # Review queue name handling
101
- grep -r "pgmq\.q_\|pgmq\.a_" lib/
102
- ```
103
-
104
- ## Common Mistakes to Avoid
105
-
106
- | Wrong | Right |
107
- |-------|-------|
108
- | String interpolation in SQL | Parameterized queries or sanitized identifiers |
109
- | Marshal.load on payloads | JSON.parse only |
110
- | Global PGMQ client without pool | Connection pool with thread safety |
111
- | Default-open dashboard | Require explicit auth configuration |
112
- | Swallowing worker errors | Log and track via failed_events table |
113
- | Infinite visibility timeout | Always set reasonable VT with DLQ fallback |
114
-
115
- ## Handoff
116
-
117
- When complete, summarize:
118
- - Vulnerabilities found (with severity)
119
- - Remediation steps
120
- - Tests to add
121
-
122
- Now, focus on security review for the current task.