Skip to content

fix: load the mailbox when the gem is required - #29

Merged
cardmagic merged 2 commits into
mainfrom
agent/reminder-scheduler-mailbox
Aug 10, 2026
Merged

fix: load the mailbox when the gem is required#29
cardmagic merged 2 commits into
mainfrom
agent/reminder-scheduler-mailbox

Conversation

@cardmagic

Copy link
Copy Markdown
Owner

Fixes the reported showstopper. Version bumped to 0.10.2.

Root cause

SolidObjects::Mailbox was never required by lib/solid_objects.rb. It was reachable only as a side effect of the caller path:

def client
  require "solid_objects/client"   # client.rb requires solid_objects/mailbox
  @client ||= Client.new
end

SolidObjects.client is called when an application makes an actor call. The standalone worker never calls it. So in solid_objects start, Mailbox is undefined, and Mailbox.new resolves through module nesting to SolidObjects::ReminderScheduler::Mailbox, which does not exist.

Reproduced in a fresh process, which is exactly how production loads:

$ ruby -e require "solid_objects"; p SolidObjects.const_defined?(:Mailbox, false)
false

The reminder scheduler was not the only casualty. effect_executor.rb calls Mailbox.new in four places, so effect success and failure messages back to actors failed the same way. Same bug, different role, also only in the standalone worker.

Neither role rescues, so the NameError kills the thread, the supervisor replaces it, the replacement claims the same reminder and dies again. The reminder is released each time, so it stays due forever. The test output shows that churn directly.

Why every test passed

Your diagnosis was exactly right. In any test process something has already loaded Mailboxclient.rb, doctor.rb, or a test file requiring it directly — so the constant resolves and the code under test never exercises the load path production uses. Passing in process says nothing about the worker.

So the tests here run the thing that actually breaks:

1. A due reminder through a real solid_objects start worker. New test/dummy/prepare_cli_reminder.rb inserts an already-due reminder; the test boots the CLI worker as a subprocess and asserts the reminder fires. Against the unfixed code it fails with the reported error, repeated once per supervisor replacement:

reminder_scheduler.rb:104:in enqueue: uninitialized constant
SolidObjects::ReminderScheduler::Mailbox (NameError)

2. A load contract test. Asks a fresh process what require "solid_objects" actually defines, and compares the set of unloaded files against an explicit list of deliberate deferrals, each with a stated reason:

DEFERRED = {
  "cli" => "loaded by exe/solid_objects, and pulls in thor",
  "client" => "the caller path, required by SolidObjects.client",
  ...
}

A file that stops being loaded now fails the suite unless someone argues in that list that no runtime role reaches it. That generalises past this one constant, which matters because the same shape already bit this codebase once before: WakeUpAdapters.for referencing DatabaseAdapter before it was required.

Both tests were verified to fail with the fix reverted.

The fix

One line in lib/solid_objects.rb, requiring the mailbox alongside the other runtime dependencies, with a comment recording why it cannot be left to the caller path.

Validation

Run Result
bundle exec rake (SQLite) 381 runs, 1278 assertions, 0 failures, 14 skips
Trilogy (docker MySQL 8) 381 runs, 1241 assertions, 0 failures, 22 skips
PostgreSQL 17.6 381 runs, 1258 assertions, 0 failures, 14 skips
Standard, RuboCop, RBS, Steep, Brakeman clean

docs/roadmap.md listed reminders under "Implemented and tested" while they were broken in the only process that runs them. Corrected to say so, and to record that they are now exercised through a real worker.

Mailbox was reachable only through the caller path, which loads it as a
side effect of SolidObjects.client. The reminder scheduler and the effect
executor enqueue through it directly and run in solid_objects start, a
process that never calls the client, so both raised NameError on every
attempt. Reminders never fired and effect result messages never
delivered, while the supervisor replaced the dying role over and over.

Every test passed because a test process has already loaded the constant
through some other path. That is the whole shape of the bug, so the
regression test runs a due reminder through a real worker, and a load
contract test asks a fresh process what requiring the gem actually
defines. A file that stops being loaded now fails that test unless it is
listed as deliberately deferred with a reason.

The roadmap listed reminders as implemented and tested while they were
broken in the only process that runs them.
@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown

Greptile Summary

The PR fixes standalone worker failures by loading SolidObjects::Mailbox from the gem’s primary entry point and bumps the release to 0.10.2.

  • Adds a fresh-process load-contract test to detect missing runtime dependencies.
  • Adds an end-to-end reminder test using a real solid_objects start subprocess.
  • Updates the changelog and roadmap to document the failure and coverage.

Confidence Score: 5/5

The PR appears safe to merge, with the mailbox dependency loaded before every standalone worker role that uses it and targeted regression coverage for the production load path.

The production change is narrowly scoped to correcting require order, and the new fresh-process and worker subprocess tests exercise the previously broken paths without exposing a concrete regression.

Important Files Changed

Filename Overview
lib/solid_objects.rb Loads the mailbox before worker roles that directly depend on it, correcting the standalone-process require order.
test/integration/cli_test.rb Adds subprocess coverage proving that a due reminder is delivered by the standalone worker.
test/integration/load_contract_test.rb Adds a fresh-process contract test that compares unloaded library files with an explicit deferred list.
test/dummy/prepare_cli_reminder.rb Creates an already-due reminder and actor instance for the standalone-worker integration test.
lib/solid_objects/version.rb Bumps the gem version from 0.10.1 to 0.10.2.

Reviews (1): Last reviewed commit: "fix: load the mailbox when the gem is re..." | Re-trigger Greptile

Every miss in recent work reduces to a test passing for a reason
unrelated to the behavior: a constant already loaded by the test process,
a double without the ensure its real collaborator runs, a mock ignoring
the signal under test, a skipped adapter test reading as a pass.

Two rules cover almost all of it, and both are checkable in review rather
than a reminder to be careful.
@cardmagic
cardmagic merged commit 99cfd5a into main Aug 10, 2026
28 checks passed
@cardmagic
cardmagic deleted the agent/reminder-scheduler-mailbox branch August 10, 2026 22:14
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.

1 participant