Conversation
pgmq-ruby had no durable memory and no .claude/ directory, so every session re-derived the same three invariants from the code: nothing is converted (no type_map_for_results, so every value is the String PostgreSQL printed), every queue name passes validate_queue_name! before it can reach the two methods that interpolate SQL, and the pool owns connection identity. The lode writes those down once, with the file and line that make each one true. lode/ holds a summary, terminology, practices and a workflow profile, four subsystem summaries (client and its nine modules, connection and transactions, models and the raw-String contract, tests and CI), the map, and lode/review/. review/ carries 22 rules across five files (client 3, connection 5, docs 3, testing 6, tooling 5), all from merged PR review threads on mensfeld/pgmq-ruby (PRs 5, 17, 27, 47, 59, 63, 78, 85, 86, 99, 119, 121, 142); there are no cubic learnings for this repository. Four comments from PR 121 merged into one rule about asserting distinct values rather than counts, three from PR 59 into one about keyword qty:, and five from PR 119 into one about wait lengths. Findings whose subject is absent from this tree were dropped: PR 9 (Message lazy deserialization), PR 59 (detach_archive), PR 63 (vt_offset), PR 119 (time-like produce delay, kept only as the set_vt shape it still applies to), PR 142 (pool reload, kept only as the YARD rule), PR 120 (indentation, fixed by rubocop -A), and the wording nits of PRs 13 and 17. Two findings the tree deliberately did not act on are recorded as "Not a rule here" entries so the next reviewer does not raise them again. Every line range and count in the lode was computed with a script, not read off the screen. Eighteen doc-versus-code disagreements found on the way are in the PR body, not in the lode. No local .claude/commands existed, so nothing was retired. .gitignore previously ignored all of .claude/; it now ignores everything under it except the checked-in settings.json that enables the plugin. Claude-Session: https://claude.ai/code/session_01Hga3idYBp7fWdj7GmmfofF
The gate's claims agent counted fourteen keys in .yard-lint.yml's Patterns hash where the rule listed eleven, and the block ends at 196, not 197. Claude-Session: https://claude.ai/code/session_01Hga3idYBp7fWdj7GmmfofF
There was a problem hiding this comment.
5 issues found across 18 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="lode/workflow.md">
<violation number="1" location="lode/workflow.md:9">
P3: The fast-loop command template `test/lib/pgmq/<name>_test.rb` does not resolve for the client operation tests (consumer, multi_queue, producer, ...), which live in `test/lib/pgmq/client/` — the same files this document cites later. A developer following this for those tests hits "No such file". Make the template cover both layouts, e.g. `test/lib/pgmq/client/<name>_test.rb` (operation modules) with a note that core tests sit directly under `test/lib/pgmq/`.</violation>
</file>
<file name=".claude/settings.json">
<violation number="1" location=".claude/settings.json:10">
P2: The team-wide gate (/lode:gate, run before every push) is loaded from the unpinned default branch of `zoolutions/claude-plugins` via `"lode@zoolutions": true`. Any breaking or changed code pushed to that marketplace repo propagates silently to every developer's next session with no review, and can start blocking all pushes. Consider pinning the plugin or marketplace to a specific version/tag (e.g. `lode@zoolutions:<version>`) so the enforcement code only changes through an explicit bump.</violation>
</file>
<file name="lode/review/docs.md">
<violation number="1" location="lode/review/docs.md:6">
P3: The `lib/pgmq/client.rb:20-25` reference omits the primary class-level example: `@example Basic usage` lives at lines 9-19, while 20-25 covers only the Rails and connection-string examples. Since this file states it was verified against the tree and is read by the gate, cite the full class-example range as `9-25` so the rule check covers all class-level examples.</violation>
</file>
<file name="lode/review/connection.md">
<violation number="1" location="lode/review/connection.md:28">
P3: The 'Proven by' note says no test reaches the substring path, but the negative case it cites contradicts that: the bare `PG::Error` test at connection_test.rb:280-283 falls past the class check at connection.rb:142 and does execute the `LOST_CONNECTION_MESSAGES.any?` scan (returning false). The accurate claim is that no test positively exercises a substring match. Reword so the evidence statement is not self-contradictory.</violation>
<violation number="2" location="lode/review/connection.md:34">
P3: The rule says all three `ConfigurationError` paths live in `normalize_connection_params` and `parse_connection_string`, but only two do: the invalid-format raise in `normalize_connection_params` and the `PG::Error` rescue in `parse_connection_string`. The third — the nil `conn_params` guard — is raised in `#initialize` (45-50), not in either method. Correct the attribution so a future reviewer looks in the right method.</violation>
</file>
Heads up: you’ve reached your flex budget. Increase your flex budget or wait for usage to reset.
Re-trigger cubic
| } | ||
| } | ||
| }, | ||
| "enabledPlugins": { |
There was a problem hiding this comment.
P2: The team-wide gate (/lode:gate, run before every push) is loaded from the unpinned default branch of zoolutions/claude-plugins via "lode@zoolutions": true. Any breaking or changed code pushed to that marketplace repo propagates silently to every developer's next session with no review, and can start blocking all pushes. Consider pinning the plugin or marketplace to a specific version/tag (e.g. lode@zoolutions:<version>) so the enforcement code only changes through an explicit bump.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .claude/settings.json, line 10:
<comment>The team-wide gate (/lode:gate, run before every push) is loaded from the unpinned default branch of `zoolutions/claude-plugins` via `"lode@zoolutions": true`. Any breaking or changed code pushed to that marketplace repo propagates silently to every developer's next session with no review, and can start blocking all pushes. Consider pinning the plugin or marketplace to a specific version/tag (e.g. `lode@zoolutions:<version>`) so the enforcement code only changes through an explicit bump.</comment>
<file context>
@@ -0,0 +1,13 @@
+ }
+ }
+ },
+ "enabledPlugins": {
+ "lode@zoolutions": true
+ }
</file context>
|
|
||
| | Purpose | Command | Notes | | ||
| |---|---|---| | ||
| | fast loop (one file) | `bundle exec ruby -Ilib:test test/lib/pgmq/<name>_test.rb` | needs Postgres+pgmq on 5433; add `-n /pattern/` for one case | |
There was a problem hiding this comment.
P3: The fast-loop command template test/lib/pgmq/<name>_test.rb does not resolve for the client operation tests (consumer, multi_queue, producer, ...), which live in test/lib/pgmq/client/ — the same files this document cites later. A developer following this for those tests hits "No such file". Make the template cover both layouts, e.g. test/lib/pgmq/client/<name>_test.rb (operation modules) with a note that core tests sit directly under test/lib/pgmq/.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/workflow.md, line 9:
<comment>The fast-loop command template `test/lib/pgmq/<name>_test.rb` does not resolve for the client operation tests (consumer, multi_queue, producer, ...), which live in `test/lib/pgmq/client/` — the same files this document cites later. A developer following this for those tests hits "No such file". Make the template cover both layouts, e.g. `test/lib/pgmq/client/<name>_test.rb` (operation modules) with a note that core tests sit directly under `test/lib/pgmq/`.</comment>
<file context>
@@ -0,0 +1,106 @@
+
+| Purpose | Command | Notes |
+|---|---|---|
+| fast loop (one file) | `bundle exec ruby -Ilib:test test/lib/pgmq/<name>_test.rb` | needs Postgres+pgmq on 5433; add `-n /pattern/` for one case |
+| full suite | `bundle exec rake test` | `Rakefile:8-13`. No network, but it needs the database. **Not safe in two worktrees at once**: both point at the same `pgmq_test` database on 5433. Queue names are `SecureRandom` so rows will not collide, but pool size and connection limits will. |
+| integration examples | `bin/integrations` (or `rake examples`, `rake examples:run`, `rake examples:run_one[name]`, `rake examples:list`) | 19 scripts, each a subprocess; same database |
</file context>
| | fast loop (one file) | `bundle exec ruby -Ilib:test test/lib/pgmq/<name>_test.rb` | needs Postgres+pgmq on 5433; add `-n /pattern/` for one case | | |
| | fast loop (one file) | `bundle exec ruby -Ilib:test test/lib/pgmq/client/<name>_test.rb` (operation modules) — core tests sit at `test/lib/pgmq/<name>_test.rb` | needs Postgres+pgmq on 5433; add `-n /pattern/` for one case | |
| Accepted review findings from merged PR threads on `mensfeld/pgmq-ruby`, rewritten as rules about the system and verified against this tree. Documentation is a build step here — `yard-lint` with `RequiredCoverage: 100` and `FailOnSeverity: convention` gates CI — so these are code rules, not style preferences. | ||
|
|
||
| ### An `@example` stays inside the API of the object it documents | ||
| - **Holds because:** the example is the first thing a reader runs, and one that mixes two objects' APIs cannot be pasted anywhere. A `Connection` method's example that calls `client.read(...)` and then `connection.reload` documents neither object; the accepted fix kept the whole example inside `connection.with_connection { |conn| conn.exec(...) }`. The same rule is why `Connection`'s class-level examples (`lib/pgmq/connection.rb:14-21`) construct a `PGMQ::Connection` and `Client`'s (`lib/pgmq/client.rb:20-25`, `51-61`) construct a `PGMQ::Client`. |
There was a problem hiding this comment.
P3: The lib/pgmq/client.rb:20-25 reference omits the primary class-level example: @example Basic usage lives at lines 9-19, while 20-25 covers only the Rails and connection-string examples. Since this file states it was verified against the tree and is read by the gate, cite the full class-example range as 9-25 so the rule check covers all class-level examples.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/review/docs.md, line 6:
<comment>The `lib/pgmq/client.rb:20-25` reference omits the primary class-level example: `@example Basic usage` lives at lines 9-19, while 20-25 covers only the Rails and connection-string examples. Since this file states it was verified against the tree and is read by the gate, cite the full class-example range as `9-25` so the rule check covers all class-level examples.</comment>
<file context>
@@ -0,0 +1,19 @@
+Accepted review findings from merged PR threads on `mensfeld/pgmq-ruby`, rewritten as rules about the system and verified against this tree. Documentation is a build step here — `yard-lint` with `RequiredCoverage: 100` and `FailOnSeverity: convention` gates CI — so these are code rules, not style preferences.
+
+### An `@example` stays inside the API of the object it documents
+- **Holds because:** the example is the first thing a reader runs, and one that mixes two objects' APIs cannot be pasted anywhere. A `Connection` method's example that calls `client.read(...)` and then `connection.reload` documents neither object; the accepted fix kept the whole example inside `connection.with_connection { |conn| conn.exec(...) }`. The same rule is why `Connection`'s class-level examples (`lib/pgmq/connection.rb:14-21`) construct a `PGMQ::Connection` and `Client`'s (`lib/pgmq/client.rb:20-25`, `51-61`) construct a `PGMQ::Client`.
+- **Where:** every `@example` in `lib/`
+- **Origin:** PR #142 review thread (accepted: "fixed in bceec49")
</file context>
| - **Holds because:** the example is the first thing a reader runs, and one that mixes two objects' APIs cannot be pasted anywhere. A `Connection` method's example that calls `client.read(...)` and then `connection.reload` documents neither object; the accepted fix kept the whole example inside `connection.with_connection { |conn| conn.exec(...) }`. The same rule is why `Connection`'s class-level examples (`lib/pgmq/connection.rb:14-21`) construct a `PGMQ::Connection` and `Client`'s (`lib/pgmq/client.rb:20-25`, `51-61`) construct a `PGMQ::Client`. | |
| - **Holds because:** the example is the first thing a reader runs, and one that mixes two objects' APIs cannot be pasted anywhere. A `Connection` method's example that calls `client.read(...)` and then `connection.reload` documents neither object; the accepted fix kept the whole example inside `connection.with_connection { |conn| conn.exec(...) }`. The same rule is why `Connection`'s class-level examples (`lib/pgmq/connection.rb:14-21`) construct a `PGMQ::Connection` and `Client`'s (`lib/pgmq/client.rb:9-25`, `51-61`) construct a `PGMQ::Client`. |
| ### `Connection` requires parameters; nil is a `ConfigurationError`, not a default | ||
| - **Holds because:** this gem reads no `ENV` of its own — `CLAUDE.md` states the user manages their own `ENV.fetch` calls — so a nil parameter set has no sensible fallback and failing at construction beats connecting to whatever `PG.connect` would guess from the environment. | ||
| - **Where:** `lib/pgmq/connection.rb#initialize` (45-50) | ||
| - **Proven by:** no test. `PGMQ::Client.new` with no argument passes `nil` straight through, and no file under `test/` or `spec/` constructs a `Client` or `Connection` with `nil`, an empty Hash or a malformed connection string — the only `ConfigurationError` assertions in the suite are the shared-connection ones at `connection_test.rb:213`, `:227`, `:235`. All three `ConfigurationError` paths in `normalize_connection_params` and `parse_connection_string` are uncovered. |
There was a problem hiding this comment.
P3: The rule says all three ConfigurationError paths live in normalize_connection_params and parse_connection_string, but only two do: the invalid-format raise in normalize_connection_params and the PG::Error rescue in parse_connection_string. The third — the nil conn_params guard — is raised in #initialize (45-50), not in either method. Correct the attribution so a future reviewer looks in the right method.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/review/connection.md, line 34:
<comment>The rule says all three `ConfigurationError` paths live in `normalize_connection_params` and `parse_connection_string`, but only two do: the invalid-format raise in `normalize_connection_params` and the `PG::Error` rescue in `parse_connection_string`. The third — the nil `conn_params` guard — is raised in `#initialize` (45-50), not in either method. Correct the attribution so a future reviewer looks in the right method.</comment>
<file context>
@@ -0,0 +1,35 @@
+### `Connection` requires parameters; nil is a `ConfigurationError`, not a default
+- **Holds because:** this gem reads no `ENV` of its own — `CLAUDE.md` states the user manages their own `ENV.fetch` calls — so a nil parameter set has no sensible fallback and failing at construction beats connecting to whatever `PG.connect` would guess from the environment.
+- **Where:** `lib/pgmq/connection.rb#initialize` (45-50)
+- **Proven by:** no test. `PGMQ::Client.new` with no argument passes `nil` straight through, and no file under `test/` or `spec/` constructs a `Client` or `Connection` with `nil`, an empty Hash or a malformed connection string — the only `ConfigurationError` assertions in the suite are the shared-connection ones at `connection_test.rb:213`, `:227`, `:235`. All three `ConfigurationError` paths in `normalize_connection_params` and `parse_connection_string` are uncovered.
+- **Origin:** PR #5 review thread
</file context>
| - **Proven by:** no test. `PGMQ::Client.new` with no argument passes `nil` straight through, and no file under `test/` or `spec/` constructs a `Client` or `Connection` with `nil`, an empty Hash or a malformed connection string — the only `ConfigurationError` assertions in the suite are the shared-connection ones at `connection_test.rb:213`, `:227`, `:235`. All three `ConfigurationError` paths in `normalize_connection_params` and `parse_connection_string` are uncovered. | |
| All three `ConfigurationError` raise paths — the nil guard in `#initialize` (45-50), the invalid-format raise in `normalize_connection_params`, and the `PG::Error` rescue in `parse_connection_string` — are uncovered. |
| - **Holds because:** libpq promises a dedicated class for a dead socket but not a stable message across OS, pooler and TLS versions, so class-matching catches the next variant without waiting for it to hit production and then be added to a list. `PG::ConnectionBad` and `PG::UnableToSend` return `true` immediately; only a bare `PG::Error` reaches the eleven-entry `LOST_CONNECTION_MESSAGES` substring scan (`lib/pgmq/connection.rb:141-146`). Adding a message to the list therefore changes nothing for those two classes. | ||
| - **Where:** `lib/pgmq/connection.rb#connection_lost_error?` (141-146), `LOST_CONNECTION_MESSAGES` (115-128) | ||
| - **Safe direction:** a false positive costs one extra retry on a fresh connection; a false negative fails the user's call. | ||
| - **Proven by:** `test/lib/pgmq/connection_test.rb:252-323` — but **no test reaches the substring path**. Six of the seven cases are positive and every one of them constructs `PG::ConnectionBad` (`:262`, `:274`, `:291`, `:299`, `:311`) or `PG::UnableToSend` (`:319`), both of which the class check answers at line 142; the only `PG::Error` case (`:280-284`) is a negative. Emptying `LOST_CONNECTION_MESSAGES` would keep this suite green, and no other file in `test/` or `spec/` names the constant or the method. |
There was a problem hiding this comment.
P3: The 'Proven by' note says no test reaches the substring path, but the negative case it cites contradicts that: the bare PG::Error test at connection_test.rb:280-283 falls past the class check at connection.rb:142 and does execute the LOST_CONNECTION_MESSAGES.any? scan (returning false). The accurate claim is that no test positively exercises a substring match. Reword so the evidence statement is not self-contradictory.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lode/review/connection.md, line 28:
<comment>The 'Proven by' note says no test reaches the substring path, but the negative case it cites contradicts that: the bare `PG::Error` test at connection_test.rb:280-283 falls past the class check at connection.rb:142 and does execute the `LOST_CONNECTION_MESSAGES.any?` scan (returning false). The accurate claim is that no test positively exercises a substring match. Reword so the evidence statement is not self-contradictory.</comment>
<file context>
@@ -0,0 +1,35 @@
+- **Holds because:** libpq promises a dedicated class for a dead socket but not a stable message across OS, pooler and TLS versions, so class-matching catches the next variant without waiting for it to hit production and then be added to a list. `PG::ConnectionBad` and `PG::UnableToSend` return `true` immediately; only a bare `PG::Error` reaches the eleven-entry `LOST_CONNECTION_MESSAGES` substring scan (`lib/pgmq/connection.rb:141-146`). Adding a message to the list therefore changes nothing for those two classes.
+- **Where:** `lib/pgmq/connection.rb#connection_lost_error?` (141-146), `LOST_CONNECTION_MESSAGES` (115-128)
+- **Safe direction:** a false positive costs one extra retry on a fresh connection; a false negative fails the user's call.
+- **Proven by:** `test/lib/pgmq/connection_test.rb:252-323` — but **no test reaches the substring path**. Six of the seven cases are positive and every one of them constructs `PG::ConnectionBad` (`:262`, `:274`, `:291`, `:299`, `:311`) or `PG::UnableToSend` (`:319`), both of which the class check answers at line 142; the only `PG::Error` case (`:280-284`) is a negative. Emptying `LOST_CONNECTION_MESSAGES` would keep this suite green, and no other file in `test/` or `spec/` names the constant or the method.
+- **Origin:** PR #99 review thread (raised, not yet addressed)
+
</file context>
| - **Proven by:** `test/lib/pgmq/connection_test.rb:252-323` — but **no test reaches the substring path**. Six of the seven cases are positive and every one of them constructs `PG::ConnectionBad` (`:262`, `:274`, `:291`, `:299`, `:311`) or `PG::UnableToSend` (`:319`), both of which the class check answers at line 142; the only `PG::Error` case (`:280-284`) is a negative. Emptying `LOST_CONNECTION_MESSAGES` would keep this suite green, and no other file in `test/` or `spec/` names the constant or the method. | |
| - **Proven by:** `test/lib/pgmq/connection_test.rb:252-323` — no test positively exercises the substring branch. Six of the seven cases are positive and every one of them constructs `PG::ConnectionBad` (`:262`, `:274`, `:291`, `:299`, `:311`) or `PG::UnableToSend` (`:319`), both of which the class check answers at line 142; the only bare `PG::Error` case (`:280-283`) is a negative that traverses the scan and returns `false`. Emptying `LOST_CONNECTION_MESSAGES` would keep this suite green, and no other file in `test/` or `spec/` names the constant or the method. |
Summary
This repository joins the shared
lodeplugin from zoolutions/claude-plugins: durable memory inlode/, a pre-PR gate that reviews every branch against the repo's own rules and learnings before anything can be pushed, and the shared workflows (/lode:lfg,/lode:review-pr,/lode:finish-prs,/lode:debug-flaky,/lode:tdd,/lode:plan) readinglode/workflow.md.lode/— summary, terminology, practices, map, and four subsystem summaries (client, connection, models, testing and CI). Every claim cites a file and method; every count was computed (28pgmq.*functions, 39 public methods of which 31 validate the queue name, 48 SQL call sites, 11 lost-connection messages, 17 unit-test files, 19 example scripts, 15 CI cells).lode/review/— 22 rules in five files, all mined from the review threads of 17 merged PRs (this repo has no cubic history): connection handling, client API shapes, test discipline, tooling and docs. Duplicates across PRs were merged; findings whose subject no longer exists were dropped and are listed in the PR notes; two "Not a rule here" entries record reasoned rejections.lode/workflow.md(106 lines) — the profile, including that the fork carries no commits of its own and sits 110 commits behind upstream at 0.6.1 versus 0.7.2, so non-fork-specific fixes go upstream..claude/settings.jsonenablinglode@zoolutions;CLAUDE.mdgains a Memory section;.gitignorenarrows.claude/to.claude/*plus!.claude/settings.json(it ignored the whole directory, which would have dropped the settings file silently) and gainslode/tmp/. No local commands existed to retire.No Ruby changes. Eighteen doc-versus-code disagreements are recorded in the lode where they apply; the ones worth knowing:
bin/consoleis referenced by three docs but does not exist;CLAUDE.mdsays 8 modules and there are 9; every file-size annotation in it is stale;ci.yml:59guards on an undefined$APT_DEPSso the step always runs.Test plan
/lode:lfgon a small change reads the profile and reaches the gateDeviations & judgment calls
/lode:seed, resumed once after a rate limit (every draft re-verified; nine corrected), then gated here..gitignorechange is the one judgment call: un-ignoring onlysettings.jsonkeeps personal.claude/*files out while the shared plugin config is tracked.LODE_SKIP_GATE=1after the gate passed: the orchestrating session runs the cached 0.1.0 push hook keyed on a different checkout. A session opened here will not need it.Gate
Rounds: 1. Findings: 0 P1, 0 P2, 2 P3 fixed. Rejected 0, deferred 0.
https://claude.ai/code/session_01Hga3idYBp7fWdj7GmmfofF
Summary by cubic
Seeds the repository's durable memory with the
lode@zoolutionsplugin and enables its pre-PR gate, which reviews every branch against the repo's own rules and learnings before anything can be pushed.lode/review/carries 22 rules across five files, all mined from 17 merged PR threads, plus two "Not a rule here" entries for deliberately rejected findings..claude/settings.jsonenables the plugin;.gitignorenow ignores personal.claude/*files while keeping the settings file tracked.CLAUDE.mdgains a Memory section;lode/workflow.mdprofiles commands, branches, CI, flake sources, and conflict rules.Written for commit ca67f5c. Summary will update on new commits.