diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 99128c0d..77046851 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,6 +8,9 @@ on:
   pull_request:
     branches:
       - master
+concurrency:
+  group: ci-${{ github.head_ref }}
+  cancel-in-progress: true
 
 jobs:
   rspec:
@@ -37,18 +40,22 @@ jobs:
       fail-fast: false
       matrix:
         ruby:
+          - '3.3'
           - '3.2'
           - '3.1'
           - '3.0'
-          - '2.7'
         rails:
+          - activerecord_7.1
           - activerecord_7.0
           - activerecord_6.1
-          - activerecord_6.0
+          - activerecord_edge
         adapter:
           - 'sqlite3:///:memory:'
           - mysql2://root:root@0/closure_tree_test
           - postgres://closure_tree:closure_tree@0/closure_tree_test
+        exclude:
+          - ruby: '3.0'
+            rails: activerecord_edge
 
     steps:
       - name: Checkout
diff --git a/.github/workflows/ci_jruby.yml b/.github/workflows/ci_jruby.yml
index 9a649045..c7d0156a 100644
--- a/.github/workflows/ci_jruby.yml
+++ b/.github/workflows/ci_jruby.yml
@@ -8,6 +8,9 @@ on:
   pull_request:
     branches:
       - master
+concurrency:
+  group: ci-${{ github.head_ref }}-jruby
+  cancel-in-progress: true
 
 jobs:
   rspec:
@@ -43,7 +46,6 @@ jobs:
           - 'sqlite3:///:memory:'
           - mysql2://root:root@0/closure_tree_test
           - postgres://closure_tree:closure_tree@0/closure_tree_test
-
     steps:
       - name: Checkout
         uses: actions/checkout@v3
diff --git a/.github/workflows/ci_truffleruby.yml b/.github/workflows/ci_truffleruby.yml
index 980de7b5..b61630fd 100644
--- a/.github/workflows/ci_truffleruby.yml
+++ b/.github/workflows/ci_truffleruby.yml
@@ -8,6 +8,9 @@ on:
   pull_request:
     branches:
       - master
+concurrency:
+  group: ci-${{ github.head_ref }}-truffleruby
+  cancel-in-progress: true
 
 jobs:
   rspec:
@@ -41,7 +44,6 @@ jobs:
         rails:
           - activerecord_7.0
           - activerecord_6.1
-          - activerecord_6.0
         adapter:
           - 'sqlite3:///:memory:'
           - mysql2://root:root@0/closure_tree_test
diff --git a/.tool-versions b/.tool-versions
index 5de817ed..f2a971aa 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1 +1 @@
-ruby 3.0.5
+ruby 3.2.2
diff --git a/Appraisals b/Appraisals
index 38e88ba3..c2847734 100644
--- a/Appraisals
+++ b/Appraisals
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
-appraise 'activerecord-6.0' do
-  gem 'activerecord', '~> 6.0.0'
+appraise 'activerecord-6.1' do
+  gem 'activerecord', '~> 6.1.0'
   platforms :ruby do
     gem 'mysql2'
     gem 'pg'
@@ -15,8 +15,8 @@ appraise 'activerecord-6.0' do
   end
 end
 
-appraise 'activerecord-6.1' do
-  gem 'activerecord', '~> 6.1.0'
+appraise 'activerecord-7.0' do
+  gem 'activerecord', '~> 7.0.0'
   platforms :ruby do
     gem 'mysql2'
     gem 'pg'
@@ -30,8 +30,8 @@ appraise 'activerecord-6.1' do
   end
 end
 
-appraise 'activerecord-7.0' do
-  gem 'activerecord', '~> 7.0.0'
+appraise 'activerecord-7.1' do
+  gem 'activerecord', '~> 7.1.0'
   platforms :ruby do
     gem 'mysql2'
     gem 'pg'
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index b8497135..00000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-leap-day
\ No newline at end of file
diff --git a/closure_tree.gemspec b/closure_tree.gemspec
index 7eac8db4..83bab80c 100644
--- a/closure_tree.gemspec
+++ b/closure_tree.gemspec
@@ -18,9 +18,9 @@ Gem::Specification.new do |gem|
   end
 
   gem.test_files  = gem.files.grep(%r{^spec/})
-  gem.required_ruby_version = '>= 2.7.7'
+  gem.required_ruby_version = '>= 3.0.0'
 
-  gem.add_runtime_dependency 'activerecord', '>= 6.0.0'
+  gem.add_runtime_dependency 'activerecord', '>= 6.1.0'
   gem.add_runtime_dependency 'with_advisory_lock', '>= 4.0.0'
 
   gem.add_development_dependency 'appraisal'
diff --git a/gemfiles/activerecord_6.0.gemfile b/gemfiles/activerecord_7.1.gemfile
similarity index 93%
rename from gemfiles/activerecord_6.0.gemfile
rename to gemfiles/activerecord_7.1.gemfile
index 7a971a36..e3e148af 100644
--- a/gemfiles/activerecord_6.0.gemfile
+++ b/gemfiles/activerecord_7.1.gemfile
@@ -2,7 +2,7 @@
 
 source "https://rubygems.org"
 
-gem "activerecord", "~> 6.0.0"
+gem "activerecord", "~> 7.1.0"
 
 platforms :mri do
   group :development do
diff --git a/spec/support/schema.rb b/spec/support/schema.rb
index 7d4513b5..30a4a992 100644
--- a/spec/support/schema.rb
+++ b/spec/support/schema.rb
@@ -16,7 +16,7 @@
   end
 
   create_table 'uuid_tags', id: false, force: :cascade do |t|
-    t.string 'uuid', unique: true
+    t.string 'uuid', primary_key: true
     t.string 'name'
     t.string 'title'
     t.string 'parent_uuid'
diff --git a/test/closure_tree/label_test.rb b/test/closure_tree/label_test.rb
index b2738172..3f36dc3d 100644
--- a/test/closure_tree/label_test.rb
+++ b/test/closure_tree/label_test.rb
@@ -12,7 +12,11 @@ def self.shared_examples(&block)
       end
 
       it "should set order_value on roots" do
-        assert_equal @expected_root_order_value, @root.order_value
+        if @expected_root_order_value.nil?
+          assert_nil @root.order_value
+        else
+          assert_equal @expected_root_order_value, @root.order_value
+        end
       end
 
       it "should set order_value with siblings" do
@@ -456,7 +460,7 @@ def roots_name_and_order
     it "should reorder old-parent siblings when a node moves to another tree" do
       f2 = Label.find_or_create_by_path %w[a2 b2 c2 d2 e2 f2]
       f3 = f2.prepend_sibling(Label.new(name: "f3"))
-      f4 = f2.append_sibling(Label.new(name: "f4"))
+      _f4 = f2.append_sibling(Label.new(name: "f4"))
       @f1.add_sibling(f2)
       assert_equal [0, 1], @f1.self_and_siblings.collect(&:order_value)
       assert_equal [0, 1], f3.self_and_siblings.collect(&:order_value)
diff --git a/test/support/query_counter.rb b/test/support/query_counter.rb
index 87059967..bd59744c 100644
--- a/test/support/query_counter.rb
+++ b/test/support/query_counter.rb
@@ -16,8 +16,9 @@ def sql_queries(&block)
 
   def assert_database_queries_count(expected, &block)
     queries = sql_queries(&block)
-    queries.count.must_equal(
+    assert_equal(
       expected,
+      queries.count,
       "Expected #{expected} queries, but found #{queries.count}:\n#{queries.join("\n")}"
     )
   end
diff --git a/test/support/tag_examples.rb b/test/support/tag_examples.rb
index 03d7e54d..edb41f57 100644
--- a/test/support/tag_examples.rb
+++ b/test/support/tag_examples.rb
@@ -442,7 +442,7 @@ def assert_parent_and_children
           c = @tag_class.find_or_create_by_path %w[A B C]
           a = c.parent.parent
           b = c.parent
-          spurious_tags = @tag_class.find_or_create_by_path %w[D E]
+          _spurious_tags = @tag_class.find_or_create_by_path %w[D E]
           assert_equal [a, b], @tag_class.with_descendant(c).to_a
         end
 
@@ -612,7 +612,7 @@ def assert_parent_and_children
         end
 
         it 'finds correctly rooted paths' do
-          decoy = @tag_class.find_or_create_by_path %w[a b c d]
+          _decoy = @tag_class.find_or_create_by_path %w[a b c d]
           b_d = @tag_class.find_or_create_by_path %w[b c d]
           assert_equal b_d, @tag_class.find_by_path(%w[b c d])
           assert_nil @tag_class.find_by_path(%w[c d])
diff --git a/tests.sh b/tests.sh
deleted file mode 100755
index f39e832b..00000000
--- a/tests.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh -ex
-
-for RMI in 2.3.1 #jruby-1.6.13 :P
-do
-  rbenv local $RMI
-  appraisal bundle install
-  for DB in mysql sqlite postgresql
-  do
-    appraisal rake spec:all WITH_ADVISORY_LOCK_PREFIX=$(date +%s) DB=$DB
-  done
-done