Skip to content

fix(ruby): reject unsupported event extras - #330

Draft
NikolayS wants to merge 2 commits into
mainfrom
agent/fix-ruby-event-contract
Draft

NikolayS wants to merge 2 commits into
mainfrom
agent/fix-ruby-event-contract

Conversation

@NikolayS

@NikolayS NikolayS commented Jul 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • remove the unsupported extra: field from Pgque::Event
  • reject unknown event keywords instead of silently discarding producer data
  • document the exact event input contract and test the database round trip

Fixes #314.

Validation

  • baseline replay of the final focused tests: 2 runs, 2 assertions, 2 expected failures
  • focused head command: ruby -Ilib -Itest test/test_send.rb --name '/^(test_send_event_object|test_event_rejects_extra_keyword_instead_of_silently_dropping_it)$/'2 runs, 8 assertions, 0 failures
  • full database-backed command: PGQUE_TEST_DSN=postgresql:///pgque_pr_rules_audit ruby -S rake test77 runs, 199 assertions, 0 failures, 0 errors, 0 skips
  • gem build, isolated install, and installed-artifact contract smoke passed
  • git diff --check passed

@NikolayS

Copy link
Copy Markdown
Owner Author

Real-user verification evidence

The original RED is retrospective because implementation and tests were committed together. Replaying the final focused tests with the base library:

PGQUE_TEST_DSN=postgresql:///pgque_pr_rules_audit \
  ruby -I/tmp/pgque-030-audit/clients/ruby/lib -Iclients/ruby/test \
  clients/ruby/test/test_send.rb \
  --name '/^(test_send_event_object|test_event_rejects_extra_keyword_instead_of_silently_dropping_it)$/'

Result: 2 runs, 2 assertions, 2 failures. On head the same focused selection produced 2 runs, 8 assertions, 0 failures.

PGQUE_TEST_DSN=postgresql:///pgque_pr_rules_audit ruby -S rake test
gem build pgque.gemspec --output /tmp/pgque-330-0.3.0.rc.1.gem

Full result: 77 runs, 199 assertions, 0 failures, 0 errors, 0 skips. The isolated installed gem accepted payload:/type: and raised ArgumentError for extra:. Head CI: 17/17 checks passed.

@NikolayS NikolayS left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REV rubric review

Automated ultrareview quota was unavailable, so I executed the five official
REV rubric prompts manually. SOC2 findings were omitted as instructed. This is
a comment-only review, not an approval.

Blocking findings

None. I found no unresolved functional, security, documentation, or test
blocker on head cdaf7259cc995656cdcf3f91a02d353dc624eb8d.

Rubric results

  • Bug hunter: removing the unsupported extra: keyword converts silent data
    loss into an immediate ArgumentError; the supported payload/type path is
    unchanged.
  • Security reviewer: no SQL construction, privilege, secret, or trust-boundary
    change is present.
  • Docs reviewer: the README example and exact accepted-keyword contract match
    the implementation and distinguish producer inputs from receive-side extras.
  • Guidelines checker: title and commits use allowed types, subjects are under
    50 characters, the scope is surgical, and all CI checks pass.
  • Test analyzer: the focused base replay demonstrates the old silent-drop
    failure, while head tests verify rejection plus a real database round trip
    for the supported event object.

Nonblocking findings

None from the manual rubrics. No code change is requested by this review.

@NikolayS

NikolayS commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

samorev Code Review Report

Pipeline Coverage
unknown Not reported

REVIEW FINDINGS (5)

HIGH MR/PR state - Review target is draft

The review target is still marked as draft.
Fix: Mark it ready for review before merge.

CRITICAL CI/Pipeline - Pipeline status is unknown

Provider CI reported status unknown.
Fix: Fix failing checks and rerun review.

MEDIUM [guidelines] Breaking public API change to a released gem class with no version bump, CHANGELOG entry, or deprecation path in the diff. Both Pgque::Event.new(..., extra: {...}) (now ArgumentError) and event.extra (now NoMethodError) break at runtime for existing callers on upgrade. This is a semver-major change shipped as fix(...), and the README wording ("The receive-side extra1 through extra4 fields are not supported by this producer wrapper") implies users plausibly were passing extra: believing it worked.

clients/ruby/lib/pgque/event.rb:
Fix: Bump the minor/major version per the gem's semver policy and add a CHANGELOG entry calling out the removal explicitly. If the gem is already published with extra:, consider a one-release deprecation (extra: nilwarn + raise only when non-empty) instead of immediate removal. Also grep -rn '\.extra\b' clients/ruby/lib clients/ruby/test to confirm no other call site reads Event#extra — the green 77-run suite makes this unlikely but does not prove it for uncovered paths.

LOW [docs] The README documents that extra1extra4 are unsupported but gives no supported alternative. Producers that need to set extras now have a hard failure and no documented path forward, and the doc does not say whether that is a permanent design decision or a not-yet-implemented gap.

clients/ruby/README.md: "The receive-side extra1 through extra4 fields are not supported by this producer wrapper; unknown keywords such as extra: raise ArgumentError."
Fix: Add one sentence pointing to the supported workaround (raw SQL insert, or whichever API does accept extras), or link the tracking issue if producer-side extras are planned — e.g. "To set extra1extra4, use ... ; producer support is tracked in #NNN."

LOW [tests] The negative test asserts against the interpreter-generated ArgumentError message rather than the behavior under test, coupling it to MRI's exact wording. The message text is not part of pgque's contract and differs across Ruby implementations (JRuby/TruffleRuby) and has changed historically across MRI versions.

clients/ruby/test/test_send.rb:
Fix: assert_raises(ArgumentError) alone already pins the contract. If you want to guard against a future **opts catch-all silently re-accepting the key, assert on shape instead — e.g. refute Pgque::Event.instance_method(:initialize).parameters.any? { |t, _| t == :keyrest } — or keep the message check but loosen it to /extra/.


Summary

Area Findings Potential Filtered
CI/Pipeline 1 0 0
Security 0 0 0
Bugs 0 0 0
Tests 0 1 0
Guidelines 0 1 0
Docs 0 1 0
Metadata 1 0 0

Note:

  • Findings: High-confidence issues (8-10/10) - blocking or non-blocking per severity
  • Potential: Medium-confidence issues (4-7/10) - review manually
  • Filtered: Low-confidence issues (0-3/10) - excluded as likely false positives
Review metadata
provider=github
kind=pr
project=NikolayS/PgQue
number=330
target=github:NikolayS/PgQue#330
state=OPEN
draft=true
diff_lines=84
diff_added=39
diff_removed=5
diff_bytes=2801
comments_count=1
commits_count=2
ci_status=unknown
ci_summary=total=18 success=17 failure=0 pending=0 other=1
prompt=.claude/commands/review-mr.md
blocking=false
posted_by=gh
no_comment=false
live_posting=posted

samorev-assisted review (AI analysis by Tanya301/samorev)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(ruby): remove silently discarded Event.extra

1 participant