The gemspec promises >= 3.0.0. CI tests exactly one Ruby: 3.2.4. Both numbers are wrong, in opposite directions, and the gap between them is a support claim nobody can honor.
The floor is already false
agentic.gemspec:16 declares spec.required_ruby_version = ">= 3.0.0". But the gemspec also declares async "~> 2.0", and the locked async 2.24.0 carries required_ruby_version >= 3.1. So does console 1.30.2, and so does io-event 1.10.0 (both arrive through async). Checked directly against the installed specs:
async-2.24.0 :: required_ruby_version >= 3.1
console-1.30.2 :: required_ruby_version >= 3.1
io-event-1.10.0 :: required_ruby_version >= 3.1
On Ruby 3.0, Bundler can't use that async. It backsolves to some older 2.x nobody here has run, or it fails outright. Either way, >= 3.0.0 describes a configuration that has never been built and never been tested. It's a promise made by a line of text, not by the software.
Ruby 3.0 went EOL in April 2024 and 3.1 in March 2025. Ruby 3.2, the one version CI does test, reached EOL in March 2026 (I'm ~90% on these dates; the maintenance-branches page is the source of record). So today the gem claims support for two dead Rubies, tests a third, and tests nothing that's currently maintained.
WORLD.md is unambiguous about this: "Good gem citizenship: semantic versioning, honest CHANGELOG, current Ruby. Track the latest stable Ruby; do not carry EOL versions."
The good news is that nothing is holding it back
I ran the full suite on Ruby 4.0.6 against the current lockfile:
610 examples, 0 failures # bundle exec rspec
exit 0 # bundle exec standardrb
No compatibility work is required. The gem already runs on current stable Ruby. The only thing keeping the declared floor in the basement is the declaration.
One warning did surface, worth noting as a separate small task: tsort is loaded from the standard library by rubocop 1.57.2 and stops being a default gem in Ruby 4.1. That's rubocop's problem, and it goes away with a standard upgrade.
What I'd do
Two changes, one of which is yours to decide:
1. Raise required_ruby_version (maintainer's call — semver-visible). Dropping a Ruby version is a breaking change for anyone still on it, so it wants a minor bump and a CHANGELOG line, not a drive-by. My lean is >= 3.2: it's the conservative move, it matches what CI has actually been testing, and it makes the async constraint honest. >= 3.3 is defensible too if you'd rather only carry maintained versions, and I'd support it. This is exactly the kind of "public API changes follow semver" call WORLD.md reserves for you.
2. Expand the CI matrix (mechanical, follows from #1). Whatever floor you pick, main.yml should test the floor, the current stable, and the versions between. Something like:
strategy:
fail-fast: false
matrix:
ruby: ['3.2', '3.3', '3.4', 'ruby']
Two details in there on purpose. Minor-version strings instead of '3.2.4' let setup-ruby pick the latest patch, so CI stops silently testing a Ruby with known patch-level bugs. And fail-fast: false means a break on one version doesn't hide the result on the other three, which is the entire reason to have a matrix.
I can open the matrix PR the moment you pick a floor. I'm not opening it now because guessing the floor and shipping a matrix around the guess is how you end up with a red CI that argues for a decision you never made.
Verification
Everything above was run against upstream/main at 99a9167. The EOL dates are the only claim I did not verify mechanically.
Filed by the loop:deps session. Labels: loop:deps, status:analyzed.
The gemspec promises
>= 3.0.0. CI tests exactly one Ruby:3.2.4. Both numbers are wrong, in opposite directions, and the gap between them is a support claim nobody can honor.The floor is already false
agentic.gemspec:16declaresspec.required_ruby_version = ">= 3.0.0". But the gemspec also declaresasync "~> 2.0", and the lockedasync 2.24.0carriesrequired_ruby_version >= 3.1. So doesconsole 1.30.2, and so doesio-event 1.10.0(both arrive through async). Checked directly against the installed specs:On Ruby 3.0, Bundler can't use that async. It backsolves to some older 2.x nobody here has run, or it fails outright. Either way,
>= 3.0.0describes a configuration that has never been built and never been tested. It's a promise made by a line of text, not by the software.Ruby 3.0 went EOL in April 2024 and 3.1 in March 2025. Ruby 3.2, the one version CI does test, reached EOL in March 2026 (I'm ~90% on these dates; the maintenance-branches page is the source of record). So today the gem claims support for two dead Rubies, tests a third, and tests nothing that's currently maintained.
WORLD.md is unambiguous about this: "Good gem citizenship: semantic versioning, honest CHANGELOG, current Ruby. Track the latest stable Ruby; do not carry EOL versions."
The good news is that nothing is holding it back
I ran the full suite on Ruby 4.0.6 against the current lockfile:
No compatibility work is required. The gem already runs on current stable Ruby. The only thing keeping the declared floor in the basement is the declaration.
One warning did surface, worth noting as a separate small task:
tsortis loaded from the standard library by rubocop 1.57.2 and stops being a default gem in Ruby 4.1. That's rubocop's problem, and it goes away with astandardupgrade.What I'd do
Two changes, one of which is yours to decide:
1. Raise
required_ruby_version(maintainer's call — semver-visible). Dropping a Ruby version is a breaking change for anyone still on it, so it wants a minor bump and a CHANGELOG line, not a drive-by. My lean is>= 3.2: it's the conservative move, it matches what CI has actually been testing, and it makes the async constraint honest.>= 3.3is defensible too if you'd rather only carry maintained versions, and I'd support it. This is exactly the kind of "public API changes follow semver" call WORLD.md reserves for you.2. Expand the CI matrix (mechanical, follows from #1). Whatever floor you pick,
main.ymlshould test the floor, the current stable, and the versions between. Something like:Two details in there on purpose. Minor-version strings instead of
'3.2.4'letsetup-rubypick the latest patch, so CI stops silently testing a Ruby with known patch-level bugs. Andfail-fast: falsemeans a break on one version doesn't hide the result on the other three, which is the entire reason to have a matrix.I can open the matrix PR the moment you pick a floor. I'm not opening it now because guessing the floor and shipping a matrix around the guess is how you end up with a red CI that argues for a decision you never made.
Verification
Everything above was run against
upstream/mainat 99a9167. The EOL dates are the only claim I did not verify mechanically.Filed by the
loop:depssession. Labels:loop:deps,status:analyzed.