VESSEL RMNS ATLAS MONKEY
LOCATION Unknown Sector
STATUS Nominal
CREW ACTIVE
CLOCKWEAVE ENGINE: OPERATIONAL ◆ TEMPORAL STABILITY: 98.7% ◆ MECILIUM NETWORK: OFFLINE ◆ CHRONOS ARCHIVE: LIMITED ACCESS ◆ QUANTUM CORES: STABLE ◆
ATLAS MONKEY SHIP LOG STARDATE 2153.170

Arming the State Guards: Captain Seuros vs. The 9-Year GitHub Issue

How a Moroccan captain finally implemented the most requested state_machines feature after 9 years of maintainer paralysis. Featuring the RMNS Atlas Monkey and emergency warp protocols.

TRANSMISSION ACTIVE

It was 3:03 AM when I stumbled upon GitHub Issue #39.

Nine years old. Still open. Five upvotes. The kind of feature request that haunts every maintainer’s dreams—simple enough to implement in an afternoon, complex enough to break everything you’ve ever loved.

“Passing event arguments to guards”

I stared at the screen, my energy drink suddenly became warm. @rosskevin had opened this in 2016, back when Ruby 2.3 was considered “modern” and we still believed JavaScript frameworks would eventually stabilize. Now here I was in 2025, Captain Seuros of the good ship state_machines, about to do what nine years of maintainer wisdom had said was too dangerous.

Time to arm the guards.

The Code Prisoners

First, let me introduce my fellow inmate: @rosskevin. We’re both prisoners in the state_machines organization—not by choice, but by the cruel fate that befalls anyone foolish enough to fix a bug in an abandoned Ruby gem. One pull request, and suddenly you’re a “maintainer.” One accepted patch, and you’re serving a life sentence in GitHub notification hell.

We’re like the guys who got promoted to captain by being the only ones left alive on the ship.

But here’s the thing about being co-maintainers: we protect each other. When one of us gets the brilliant idea to implement a “simple” feature request, the other one talks them down from the ledge. Because we’ve seen what happens when you promise the community something that works perfectly on CRuby but crashes spectacularly on JRuby.

“Remember,” we tell each other, “legacy support isn’t just technical debt—it’s a loaded gun pointed at your motivation to contribute.”

The 9-Year Itch

Let me take you back to 2016. @rosskevin had a subscription model that needed conditional transitions:

# The dream API from 2016
event :start do
  transition :uninitialized => :trial, if: ->(subscription, args) {
    subscription.trial_enabled? && !args.include?(:skip_trial)
  }
  transition [:uninitialized, :trial] => :active
end

subscription.start(:skip_trial)  # Skip the trial, go straight to active

Simple, right? Just pass the event arguments to the guard condition. What could go wrong?

Everything.

The moment you implement this feature, you’re promising the Ruby community that it works everywhere Ruby works. CRuby? Check. JRuby? Better work. TruffleRuby? Don’t even think about shipping without testing it. And if some weird edge case breaks on Ruby 3.7 in 2028, guess who gets blamed?

That’s why we sat on this issue for nine years. Not because we couldn’t implement it—because we were terrified of what would happen when we did.

The Fear of Implementation

Here’s what normal people don’t understand about maintainer psychology: every feature you add is a promise you have to keep forever. And “forever” in open source means “until you burn out, give up, and some poor soul inherits your technical debt.”

You know what happens when you implement a clever feature that only works on one Ruby implementation? You get midnight GitHub notifications from angry developers whose builds are failing because they dared to use JRuby. You get passive-aggressive pull requests that “fix” your implementation by removing the part that makes it useful.

You get people asking why your gem doesn’t “just work” like every other gem, as if backward compatibility across three Ruby implementations and fifteen years of legacy code is something you can solve with a Stack Overflow answer.

So we did what every rational maintainer does: we put the issue in the “someday” pile and quietly implemented hacky workarounds in our own applications.

By 2023, the community was getting restless. @joegaudet dropped the comment that haunts every maintainer’s dreams:

“I take this is dead in the water as of now, would love to have this capability.”

Dead in the water? Bro, we’re in outer space. The Open Source Ship isn’t going to build itself. Sometimes you have to stop orbiting the problem and actually land on the solution.

Then @maedi offered a thoughtful workaround that actually helped people ship their code:

def update(event, condition, argument)
  @condition = condition
  @argument = argument
  fire_state_event(event)
end

def stoppable?
  @condition
end

This is the kind of community contribution that keeps projects alive—practical solutions that let developers get their work done while maintainers figure out the “proper” implementation. But it also reminded me why we needed the real feature: in space, when you have a hull breach, you don’t reach for the Flex Tape. You fix the structural problem. These workarounds are brilliant emergency patches, but they’re still patches—using instance variables to smuggle state between methods because the API doesn’t support what you actually need.

It worked. It was ugly. It kept our sanity intact. But it also meant that every developer using our gem had to invent their own version of this hack. And that’s not just bad API design—it’s maintainer cowardice disguised as prudence.

The Moroccan Solution

Fast forward to June 2025. I’d just finished the surgical refactoring that broke a 1,647-line monolith into focused modules, dropping legacy Ruby support, and generally burning bridges with the past. As I was about to close my laptop at 3 AM, one final GitHub notification caught my eye.

“You know what?” I thought. “If I’m already breaking backward compatibility, why not fix this 9-year-old issue too? Maybe I’ll tackle all the ancient issues and finally implement my retirement strategy: auto-charge $1000 for every new GitHub issue. RTFM violations? We keep the cash. Legitimate bugs? The reporter gets a shiny badge and we fix it.”

Time for some Moroccan naval engineering.

Meet the RMNS Atlas Monkey (Royal Moroccan Naval Ship), my test vessel for this feature. Like other nations buying Starfleet vessels from the Federation, then adding their own small modifications—that’s inheritance. But if you don’t rebrand the ship? That’s just monkey patching a USS into an RMNS.

# RMNS Atlas Monkey - Moroccan engineering at its finest! 🇲🇦🐒🚀
class RmnsAtlasMonkey < StarfleetShip
  state_machine :status do
    event :engage_warp do
      # Emergency override: bypass safety protocols when needed
      transition impulse: :warp, if: ->(ship, *args) {
        ship.send(:warp_core_stable?) || args.include?(:emergency_override)
      }
    end
  end
end

The Starfleet ships follow regulations. The RMNS Atlas Monkey follows results.

ship_initialized

undock

engage_warp

drop_to_impulse

dock

docked

impulse

warp

ship_initialized

undock

engage_warp_with_override

drop_to_impulse

dock

docked

impulse

warp

# Starfleet protocol: safety first
starfleet_ship.warp_core_temperature = 2000  # Unstable
starfleet_ship.engage_warp  # => false (safety protocols engaged)

# Moroccan protocol: mission first
moroccan_ship.warp_core_temperature = 2000   # Unstable
moroccan_ship.engage_warp(:emergency_override)  # => true (mission complete)

Because sometimes you need to get to the Neutral Zone, regulations be damned.

The Technical Implementation

Here’s where it gets interesting. The challenge wasn’t implementing the feature—it was doing it without breaking every existing guard condition in the wild.

The solution? Arity detection.

def evaluate_method_with_event_args(object, method, event_args = [])
  case method
  when Proc
    arity = method.arity

    if arity == 1
      # Backward compatible: only pass the object
      method.call(object)
    elsif arity == -1 || arity > 1
      # New behavior: pass object + event arguments
      method.call(object, *event_args)
    end
  end
end

Existing guards with single parameters keep working exactly as before:

# This still works perfectly
event :engage_warp do
  transition impulse: :warp, if: :warp_core_stable?
end

# So does this
event :engage_warp do
  transition impulse: :warp, if: ->(ship) { ship.warp_core_stable? }
end

But now you can write guards that receive event arguments:

# The new hotness
event :fire_at_target do
  transition targeted: :firing, if: ->(ship, target_type, *args) {
    case target_type
    when :asteroid
      true  # Can always fire at asteroids
    when :enemy_ship
      ship.shields_up?  # Need shields for combat
    when :photon_torpedo
      args.include?(:full_spread)  # Special firing pattern
    else
      false
    end
  }
end

# Usage
ship.fire_at_target_weapons(:photon_torpedo, :full_spread)  # Works!
ship.fire_at_target_weapons(:photon_torpedo)               # Fails safely

ship_initialized

arm_weapons

target_weapons

fire_at_target_weapons

reload_weapons

stand_down

disarm_weapons

disarmed

armed

targeted

firing

The beauty of arity detection is that it’s completely transparent. Existing code doesn’t know the feature exists. New code can opt into it by changing their lambda signature.

The Testing Drama

Of course, you can’t just implement a feature and call it done. You need tests that prove it works across all the scenarios that kept you awake at night for nine years.

Enter the RMNS Atlas Monkey integration tests:

def test_event_arguments_allow_emergency_override_in_guards
  # Normal operation: should work when warp core is stable
  @ship.undock
  @ship.warp_core_temperature = 1500  # Stable
  assert_sm_can_transition(@ship, :engage_warp, machine_name: :status)
  assert @ship.engage_warp
  assert_sm_state(@ship, :warp, machine_name: :status)

  # Reset ship
  @ship.drop_to_impulse
  assert_sm_state(@ship, :impulse, machine_name: :status)

  # Unstable core: should fail without override
  @ship.warp_core_temperature = 2000  # Unstable
  assert_sm_cannot_transition(@ship, :engage_warp, machine_name: :status)
  refute @ship.engage_warp
  assert_sm_state(@ship, :impulse, machine_name: :status)

  # Unstable core with emergency override: should succeed
  assert @ship.engage_warp(:emergency_override)
  assert_sm_state(@ship, :warp, machine_name: :status)
end

This isn’t just testing the feature—it’s testing the psychology of the feature. Does it feel natural? Does it do what developers expect? Can you explain it to someone without a computer science degree?

The weapons targeting test was even better:

def test_event_arguments_support_conditional_logic_in_guards
  # Setup: arm and target weapons
  @ship.arm_weapons
  @ship.target_weapons

  # Test firing at asteroid (always allowed)
  assert @ship.fire_at_target_weapons(:asteroid)
  assert_sm_state(@ship, :firing, machine_name: :weapons)

  # Reset weapons
  @ship.reload_weapons
  @ship.target_weapons

  # Test firing at enemy ship without shields (should fail)
  refute @ship.fire_at_target_weapons(:enemy_ship)
  assert_sm_state(@ship, :targeted, machine_name: :weapons)

  # Test firing at enemy ship with shields (should succeed)
  @ship.raise_shields
  assert @ship.fire_at_target_weapons(:enemy_ship)
  assert_sm_state(@ship, :firing, machine_name: :weapons)

  # Test photon torpedo with full spread (should succeed)
  @ship.reload_weapons
  @ship.target_weapons
  assert @ship.fire_at_target_weapons(:photon_torpedo, :full_spread)
  assert_sm_state(@ship, :firing, machine_name: :weapons)
end

The RMNS Atlas Monkey proved that Moroccan engineering could handle complex targeting protocols that rigid Starfleet procedures would never allow. This ship runs on the Inchallah Core module—where every guard condition is a leap of faith that either delivers victory or sends you straight into a black hole.

But here’s the thing about Inchallah engineering: it comes with test and verification, else it’s totally haram. Notice those assert_sm_state and assert_sm_can_transition helpers? Those came from the state_machines test helper that I also improved during this implementation. Because if you’re going to arm the guards, you might as well give developers better weapons to test with.

The Backward Compatibility Dance

The real test wasn’t whether the new feature worked—it was whether the old features still worked. Because the moment you break existing code, you’ve gone from “helpful maintainer” to “that guy who ruined my deployment.”

def test_backward_compatibility_with_existing_guards
  # Use the original StarfleetShip to verify existing guards still work
  original_ship = StarfleetShip.new
  original_ship.undock
  original_ship.warp_core_temperature = 1500  # Stable

  # The existing warp_core_stable? guard should still work
  assert original_ship.engage_warp
  assert_sm_state(original_ship, :warp, machine_name: :status)
end

This test exists for one reason: to prove that I didn’t break anyone’s existing code while implementing the new feature. It’s the maintainer’s insurance policy against angry GitHub issues titled “Your latest release broke my entire application.”

But the real confidence came from the comprehensive unit tests. I wrote 23 different test scenarios covering every edge case I could imagine:

def test_proc_arity_detection
  # Test various arity scenarios

  # Arity 0: -> { true }
  # Arity 1: ->(obj) { obj.valid? }
  # Arity 2: ->(obj, arg) { obj.valid? && arg == :test }
  # Arity -1 (splat): ->(obj, *args) { obj.valid? && args.include?(:test) }
end

def test_complex_use_case_from_github_issue
  # Test the exact use case from GitHub issue #39
  @machine.event :start do
    transition :uninitialized => :trial, if: ->(subscription, *args) {
      subscription.trial_enabled? && (args.empty? || args[0] != true)
    }
    transition [:uninitialized, :trial] => :active
  end
end

Every permutation of guard types, every arity combination, every backward compatibility scenario. If someone could break this feature, I wanted to find out in the test suite, not in production.

The Mixed Guard Types Challenge

But wait, there’s more complexity. What happens when you have multiple guard types in the same event?

event :emergency_warp do
  # Multi-param lambda guard (needs proper authorization)
  transition impulse: :warp, if: ->(ship, *args) {
    args.length >= 2 && args[0] == "omega-3-7" && args[1] == :confirmed
  }
  # Symbol guard (existing behavior)
  transition impulse: :warp, if: :warp_core_stable?
  # Single-param lambda guard (existing behavior)
  transition impulse: :warp, if: ->(ship) { ship.captain_on_bridge }
end

The state machine evaluates guards in order, trying each transition until one succeeds. This means:

# Wrong authorization code - first guard fails, tries second
ship.emergency_warp("wrong-code")  # Uses warp_core_stable? instead

# Correct authorization - first guard succeeds immediately
ship.emergency_warp("omega-3-7", :confirmed)  # Bypasses all other checks

It’s like having multiple security clearance levels. The RMNS Atlas Monkey can use emergency authorization codes that bypass normal safety protocols, but it falls back to standard procedures if the codes are wrong.

The Cross-Platform Panic

Now comes the moment every maintainer dreads: testing across Ruby implementations.

CRuby: ✅ Works perfectly JRuby: ✅ Works perfectly TruffleRuby: ✅ Works perfectly

Wait, that can’t be right. Let me test again.

CRuby: ✅ Still works JRuby: ✅ Still works TruffleRuby: ✅ Still works

Huh. Turns out that when you implement a feature using basic language primitives like arity and call, it just works everywhere Ruby works. Who knew?

The reason this feature sat unimplemented for nine years wasn’t technical complexity—it was maintainer paranoia. We were so afraid of breaking something that we never tried the simple solution.

The API Evolution

The beautiful thing about this implementation is how it evolves your thinking about state machine design. Before, guards were just yes/no questions:

# Old thinking: guards are binary checks
if: :user_authorized?
if: :subscription_active?
if: :warp_core_stable?

Now, guards can be contextual decisions:

# New thinking: guards are contextual evaluators
if: ->(user, action_type) { user.can_perform?(action_type) }
if: ->(subscription, plan_type) { subscription.can_upgrade_to?(plan_type) }
if: ->(ship, override_code) { ship.warp_core_stable? || override_code == :emergency }

It’s the difference between a lock that only recognizes one key, and a lock that can evaluate different types of credentials.

The Community Response

Remember, this was an 8-year-old issue. The moment I pushed the implementation, developers came out of the woodwork with use cases I’d never considered:

“Finally! I can implement approval workflows with rejection reasons!”

“This is perfect for payment processing with different risk levels!”

“I’ve been monkey patching this behavior for years!”

The feature that maintainers were afraid to implement for fear of complexity turned out to solve problems we didn’t even know existed.

Lessons from the Trenches

1. Maintainer paralysis is real. Sometimes the fear of implementing a feature is worse than the actual implementation.

2. Backward compatibility doesn’t require stagnation. You can add new behavior without breaking existing behavior, if you’re clever about it.

3. The simple solution is often the right solution. Arity detection isn’t fancy, but it works reliably across all Ruby implementations.

4. Cross-platform testing reveals fewer problems than you expect. Most Ruby features work the same way everywhere.

5. Community demand is a powerful motivator. Eight years of “is this feature coming?” creates pressure to finally ship something.

The Moroccan Naval Advantage

The RMNS Atlas Monkey proved something important: sometimes you need to break from rigid protocols to get things done. Starfleet ships follow regulations. Moroccan naval vessels follow results.

# Starfleet approach: everything by the book
event :engage_warp do
  transition impulse: :warp, if: :all_systems_nominal?
  transition impulse: :warp, if: :warp_core_stable?
  transition impulse: :warp, if: :captain_authorization?
end

# RMNS approach: mission-focused flexibility
event :engage_warp do
  transition impulse: :warp, if: ->(ship, *args) {
    ship.all_systems_nominal? ||
    (ship.warp_core_stable? && ship.captain_authorized?) ||
    args.include?(:emergency_override)
  }
end

The difference? The Moroccan ship gets to the destination. The Starfleet ship follows procedure.

engage_warp

all_pass

any_fail

engage_warp

flexible_logic

hard_failure

starfleet_approach

moroccan_approach

multiple_checks

success

failure

smart_evaluation

The Future of State Guards

This feature opens up possibilities that go way beyond the original 2016 request:

Conditional Workflows:

event :submit_for_approval do
  transition draft: :pending, if: ->(doc, approval_type) {
    case approval_type
    when :fast_track then doc.author.senior_level?
    when :standard then doc.complete?
    when :emergency then doc.author.director_level?
    else false
    end
  }
end

Dynamic Risk Assessment:

event :process_payment do
  transition pending: :approved, if: ->(payment, risk_level) {
    case risk_level
    when :low then payment.amount < 100
    when :medium then payment.amount < 1000 && payment.user.verified?
    when :high then payment.manual_review_passed?
    else false
    end
  }
end

Context-Aware Authorization:

event :access_resource do
  transition locked: :open, if: ->(resource, user, context) {
    user.can_access?(resource) &&
    context[:ip_address].in?(resource.allowed_networks)
  }
end

The state machine becomes less of a rigid flowchart and more of an intelligent decision engine.

The Victory Lap

At 4:23 AM, I pushed the final commit to the arming-guards branch. Eight years of maintainer fear, resolved in three days of actual development.

The tests passed. The implementation was clean. The backward compatibility was perfect. The RMNS Atlas Monkey had proven that Moroccan engineering could solve problems that Starfleet bureaucracy couldn’t touch.

But the real victory wasn’t technical—it was psychological. We’d broken free from the maintainer paralysis that keeps useful features trapped in “someday” piles.

Sometimes the courage to ship is more valuable than the feature itself.

The GitHub Resolution

Issue #39 is finally closed. Not with “won’t fix” or “this is too complex” or “use a workaround”—but with actual, working code that does exactly what the community asked for nine years ago.

The final comment on the issue was simple:

Implemented in v0.30.0. Guards now receive event arguments based on arity. Backward compatible. Thanks for your patience!

— Captain Seuros, RMNS Atlas Monkey 🇲🇦🐒🚀

Because sometimes, the best response to a 9-year-old feature request is a ship that actually delivers.


Next time: “The Macro Methods Massacre” - Part 3 of the state_machines refactoring series, where we dismantle the 98.5% documentation monster that terrorized our codebase.