Skip to main content

Task Breakdown — RFC Phase 2: Rails 7.2.3.1 → 8.1.3 (Track 2a); Ruby 4.0.5 deferred (Track 2b)

Source RFC: phase-2-rails-8-1-ruby-4-0-upgrade.md Slicing: Vertical (work-item) — horizontal UI/API phases n/a for an infra upgrade · Blocked tasks: included inline · Repos: chatbot only (BE/Infra; the asset-pipeline tasks are FE-flavored work inside the same repo, tagged [FE])

Paths/lines are grounded against the real chatbot tree as of 2026-07-13 — the source RFC's and Phase 1 RFC's already-verified citations, plus a few additional greps run for this breakdown (config/initializers/rails_admin.rb:12, Dockerfile:1/Dockerfile.nonroot:1 node image, app/javascript/packs/application.js:7,12, app/views/layouts/{application,login}.html.erb). Nothing invented; unverified items stay flagged [unverified — check repo]. Track 2b (Ruby 4.0.5) is scope-excluded from actionable tasks per the RFC's own "recommend DEFER" stance — see Skipped / deferred below.

Effort Summary

Wave / stageBE daysFE daysQA daysTotal
Rails 8.0.5 bump + app:update review (Task 1)213
Gem wave — devise/paper_trail/rspec-rails/searchkick/acts_as_paranoid/grape (Task 2)2.513.5
Sidekiq 8 + Sidekiq Pro 8 (Task 3)213
load_defaults 7.2→8.0 stepping (Task 4)1.512.5
8.0 checkpoint validation (Task 5)11.52.5
Rails 8.1.3 bump + load_defaults 8.0→8.1 (Task 6)2.51.54
rails_admin-on-8.1 verification spike (Task 7)1.50.52
Asset-pipeline workstream (Tasks 8–9)6.52.59
Final validation, staging bake, canary (Task 10)21.53.5
Grand total156.511.533

Confidence: medium. Assumptions: (1) rails_admin (Task 7) passes its Rails 8.1 spike without needing a fork/patch — if it does, add rework; (2) Sidekiq Pro 8 registry/license access resolves favorably (Task 3 is ⚠️ partially blocked on this, not assumed); (3) the asset-pipeline workstream (Tasks 8–9) starts in parallel with/soon after Phase 1 per the RFC's own recommendation, so its effort here assumes that head start, not a cold start mid-Track-2a. Track 2b (Ruby 4.0.5) is fully deferred — no effort estimated this cycle; see Skipped / deferred below.


Tasks (Track 2a, ordered by execution-plan dependency)

Task 1: [BE] Rails 8.0.5 bump PR + app:update review

The app boots on Rails 8.0.5 with load_defaults still at 7.2 — the same "boot first, flip defaults later" discipline as Phase 1.

Status: ✅ Actionable once Phase 1 lands (Ruby 3.4.10 / Rails 7.2.3.1 / load_defaults 7.2).

Implementation Plan

ActionFileWhat changes
editGemfile:9~> 7.2.3.1~> 8.0.5
n/a (generated)review every app:update diff; consciously decline Propshaft scaffolding if staying on Sprockets

Implementation steps

  1. Bump Gemfile:9, bundle update rails; confirm acts_as_paranoid 0.10.3 (from Phase 1) still resolves — its < 8.1 ceiling already covers 8.0.
  2. Run bin/rails app:update; review every generated/changed file, explicitly declining any Propshaft/Solid Queue/Kamal scaffolding (new-app defaults, not forced on this app).
  3. Boot every process type with load_defaults unchanged at 7.2; run full rspec.

Acceptance criteria

  • App boots on Rails 8.0.5 with load_defaults unchanged at 7.2.
  • app:update diff explicitly reviewed; Propshaft/Solid Queue scaffolding declined, not silently kept.
  • Full rspec suite green.

Test strategy: Full existing rspec suite + a manual boot smoke per process type.

Effort estimate

DisciplineDays
Backend2
QA1
Total3

Sprockets stays supported (no upper Rails cap) — no forced asset-pipeline change in this task.

Run to verify

bundle exec rspec && bin/rails app:update --dry-run

Depends on: Phase 1 complete. Unblocks Tasks 2–4.


Task 2: [BE] Gem wave — devise, paper_trail, rspec-rails, searchkick, acts_as_paranoid, grape check

The remaining Rails-8-line gem blockers land as their own small deploys, same wave-0 discipline as Phase 1.

Status: ✅ Actionable

Implementation Plan

ActionFileWhat changes
editGemfile:130devise~> 5.0.4 (4.9.x has route-registration failures on 8.1)
editGemfile:146paper_trail~> 17
editGemfile:65rspec-rails~> 8.0.4 (requires railties >= 7.2)
editGemfile:164searchkick~> 6.1.2 (requires activemodel >= 7.2)
editGemfile:109acts_as_paranoid~> 0.11.0 (ceiling → < 8.2)
verifyGemfile:180grape ~> 2.0.0 — confirm 2.4.x (from Phase 1 wave-0) still resolves on Rails 8.1; no bump forced

Implementation steps

  1. Bump devise; re-run Devise routing/controller specs (route-registration is the known 4.9.x failure mode on 8.1).
  2. Bump paper_trail, rspec-rails, searchkick; re-run their respective spec suites.
  3. Bump acts_as_paranoid to 0.11.0; confirm the ceiling now covers 8.1 (< 8.2).
  4. Confirm grape (2.4.x from Phase 1) resolves against Rails 8.1 with no forced 2.x→3.x jump.

Acceptance criteria

  • All 5 gems bump cleanly; grape confirmed resolving with no bump needed.
  • Devise routing specs pass (the specific 4.9.x→8.1 failure mode is closed).

Test strategy: RSpec across spec/models (paper_trail/acts_as_paranoid), Devise controller/routing specs, searchkick model specs, plus bundle exec rspec (rspec-rails itself).

Effort estimate

DisciplineDays
Backend2.5
QA1
Total3.5

QA 1 — devise route/auth behavior is the highest-risk item in this wave.

Run to verify

bundle exec rspec spec/models/ spec/controllers/

Depends on: Task 1 (Rails 8.0.5 booted).


Task 3: [BE] Sidekiq 8 + Sidekiq Pro 8

Background job processing runs on the Sidekiq 8 line required alongside Rails 8.1.

Status: ⚠️ Partially blocked — the sidekiq 8.x bump itself is actionable now; Sidekiq Pro 8's registry/license access is unconfirmed (open question). Redis server ≥ 7.0 prerequisite is already met (Sidekiq's Redis is 7.x, stakeholder-confirmed 2026-07-13) — no infra upgrade needed, only a re-confirm of exact patch version at execution time.

Implementation Plan

ActionFileWhat changes
editGemfile:168sidekiq~> 8.x
editGemfile:172sidekiq-pro8.x (pending registry/license confirmation)
n/aconfig/initializers/sidekiq.rbcheck Sidekiq::ActiveJob::Wrapper class rename affecting any dashboard/queue query

Implementation steps

  1. Confirm sidekiq-pro 8 is available on the team's gems.contribsys.com registry access before starting (blocking sub-item — see Depends on).
  2. Bump sidekiq/sidekiq-pro; re-confirm Redis server patch version + any managed-service constraint (prerequisite itself already satisfied).
  3. Grep for any code/dashboard query referencing the old Sidekiq::ActiveJob::Wrapper class name; update for the 8.x rename.
  4. Boot Sidekiq; re-run worker specs; enqueue/process a test job end-to-end.

Acceptance criteria

  • sidekiq 8.x + sidekiq-pro 8.x resolve and boot cleanly.
  • No dashboard/query breaks on the Sidekiq::ActiveJob::Wrapper rename.
  • Worker specs pass; a test job enqueues and processes successfully.

Test strategy: Worker specs + a manual end-to-end job enqueue/process smoke test.

Effort estimate

DisciplineDays
Backend2
QA1
Total3

Blocked sub-item (Pro-8 registry access) is a licensing question, not engineering effort — this estimate covers the code-side bump only.

Run to verify

bundle exec rspec spec/workers/

Depends on: Task 1. Blocked on: Sidekiq Pro 8 registry/license confirmation (Open Question 1).


Task 4: [BE] load_defaults stepping — Rails 7.2 → 8.0

The app runs under the full Rails 8.0 default set, generated and reviewed the same way as Phase 1's stepping (the 8.0/8.1 defaults diff isn't centrally published — rely on the generated file).

Status: ✅ Actionable

Implementation Plan

ActionFileWhat changes
create (generated)config/initializers/new_framework_defaults_8_0.rbvia bin/rails app:update
editconfig/application.rb:137.28.0

Implementation steps

  1. Generate the file via app:update; review its settings (no centrally-published diff to compare against — the file itself is the source of truth).
  2. Uncomment, deploy with defaults still 7.2 to check load, then flip to 8.0; deploy.
  3. Monitor the same class of mixed-version-pod cache/cookie risk as every prior defaults step.

Acceptance criteria

  • File generated, reviewed, fully active; load_defaults 8.0 deploys cleanly.
  • Full rspec suite green.

Test strategy: Full rspec suite + rolling-deploy monitoring, same discipline as Phase 1.

Effort estimate

DisciplineDays
Backend1.5
QA1
Total2.5

Assumes no surprise breaking default in the generated file.

Run to verify

bundle exec rspec

Depends on: Task 1. Blocks Task 5.


Task 5: [BE] 8.0 checkpoint validation

Rails 8.0.5 is fully validated as a short, deliberate checkpoint before proceeding to 8.1 — the "compress the gap" step from the RFC's direct-jump assessment, not a resting stop.

Status: ✅ Actionable

Implementation Plan

ActionFileWhat changes
n/avalidation only — full rspec, boot smoke, staging soak on Rails 8.0.5

Implementation steps

  1. Run the full rspec suite on the Rails 8.0.5 / load_defaults 8.0 branch.
  2. Boot-smoke every process type (Puma, Sidekiq, Karafka, console, cron, migration job).
  3. Brief staging soak — confirm no regression before starting the 8.1 bump (Task 6).

Acceptance criteria

  • Full rspec suite green; every process type boots cleanly on 8.0.5.
  • Staging soak passes with no open regression before Task 6 starts.

Test strategy: Full rspec suite + staging soak checklist, deliberately brief (checkpoint, not a resting stop).

Effort estimate

DisciplineDays
Backend1
QA1.5
Total2.5

QA-heavy relative to BE — this task is pure validation, no code change.

Run to verify

bundle exec rspec && bundle exec karafka console

Depends on: Tasks 2, 3, 4 landed. Blocks Task 6.


Task 6: [BE] Rails 8.1.3 bump PR + load_defaults 8.0 → 8.1 stepping

The app boots on Rails 8.1.3 with defaults fully stepped to 8.1 — the stable landing point with security runway to 2027-10-10.

Status: ✅ Actionable

Implementation Plan

ActionFileWhat changes
editGemfile:9~> 8.0.5~> 8.1.3
n/a (generated)review app:update diff again
create (generated)config/initializers/new_framework_defaults_8_1.rbvia app:update
editconfig/application.rb:138.08.1

Implementation steps

  1. Bump Gemfile:9, bundle update rails; run app:update, review the diff.
  2. Note: schema.rb columns are now dumped alphabetically as of 8.1 — expect a large, purely cosmetic one-time diff; review it explicitly so a real change isn't hidden inside the noise.
  3. Generate/review/uncomment new_framework_defaults_8_1.rb; deploy with defaults at 8.0 first, then flip to 8.1.
  4. Full rspec + per-process boot checks.

Acceptance criteria

  • App boots on Rails 8.1.3 with load_defaults fully at 8.1.
  • The schema.rb alphabetical-column diff is reviewed and confirmed cosmetic-only.
  • Full rspec suite green.

Test strategy: Full rspec suite + a manual schema-diff review + per-process boot checks.

Effort estimate

DisciplineDays
Backend2.5
QA1.5
Total4

The largest single-task effort in Track 2a — two framework bumps' worth of review compressed together (version + defaults).

Run to verify

bundle exec rspec && bin/rails app:update --dry-run

Depends on: Task 5 (8.0 checkpoint passed). Unblocks Task 7, Task 10.


Task 7: [BE] rails_admin-on-8.1 verification spike

Confirms rails_admin 3.3.0 (gemspec allows rails >= 6.0, < 9, but Rails 8.1 is not blessed upstream) actually works on Rails 8.1 before relying on it in production.

Status: ✅ Actionable as a spike — not blessed upstream, so treat as a verification task, not an assumption.

Implementation Plan

ActionFileWhat changes
verifyconfig/initializers/rails_admin.rb:12admin UI boots/functions on Rails 8.1 with config.asset_source = :webpacker (or its Task 8/9 successor)

Implementation steps

  1. Boot the admin UI on the Rails 8.1.3 branch; exercise CRUD on a representative model.
  2. Confirm config.asset_source (currently :webpacker, config/initializers/rails_admin.rb:12) still resolves assets correctly — coordinate timing with Tasks 8–9's asset-pipeline migration.
  3. If it fails: assess whether a fork/patch is needed, or whether the asset-pipeline migration (Tasks 8–9) must land first to unblock it.

Acceptance criteria

  • rails_admin boots and CRUDs correctly on Rails 8.1.3.
  • Outcome documented: works as-is, needs a fork/patch, or is blocked on the asset-pipeline migration.

Test strategy: Manual functional spike against a representative model in the admin UI — no existing automated coverage for rails_admin itself.

Effort estimate

DisciplineDays
Backend1.5
QA0.5
Total2

Spike, not a full build — effort covers the investigation, not a fix if one turns out needed.

Run to verify

bundle exec rails server # manually exercise /admin

Depends on: Task 6. Coordinate with Tasks 8–9 (asset_source coupling).


Task 8: [FE] Webpacker → shakapacker migration + Node 16 image bump

The JS build pipeline runs on a maintained successor to the retired Webpacker 5, on a supported Node line.

Status: ✅ Actionable — the highest-effort item of Track 2a; should start during/right after Phase 1, not be squeezed into this phase's tail.

Implementation Plan

ActionFileWhat changes
editGemfile (webpacker line)webpacker ~> 5.0shakapacker (latest)
editconfig/webpacker.ymlmigrate to config/shakapacker.yml per shakapacker's config-rename guide
editDockerfile:1, Dockerfile.nonroot:1node:16-alpine → a maintained Node LTS (Node 16 is itself EOL)
editDockerfile:68-69, Dockerfile.nonroot:95-96webpacker:compileshakapacker:compile in the build stages

Implementation steps

  1. Add shakapacker, run its install generator, migrate config/webpacker.ymlshakapacker.yml per the official webpacker→shakapacker guide.
  2. Bump the Node base image in both Dockerfile and Dockerfile.nonroot (and Dockerfile.develop, already Phase-1-fixed for Ruby) past Node 16's EOL.
  3. Update both Dockerfiles' asset-compile build steps from webpacker:compile to shakapacker:compile.
  4. Confirm Task 1 (Phase 1)'s CI asset-precompile gate still passes against the new pipeline.

Acceptance criteria

  • shakapacker replaces webpacker; asset compile succeeds in both Dockerfiles.
  • Node base image is on a maintained LTS line, not Node 16.
  • Visual/functional parity confirmed on the main app layouts.

Test strategy: CI asset-precompile gate (from Phase 1) + a manual visual regression pass on the main app's rendered pages.

Effort estimate

DisciplineDays
Frontend4
QA1.5
Total5.5

Highest-effort single item in Track 2a per the RFC's own framing; assumes shakapacker's official migration guide covers this app's webpacker config without a custom escape hatch.

Run to verify

bin/shakapacker:compile

Depends on: Phase 1's asset-precompile CI gate. Can start in parallel with/after Phase 1.


One Hotwire/Turbo stack across the app instead of two competing systems (legacy Turbolinks in the main layouts, turbo-rails already pulled in transitively by rails_admin).

Status: ✅ Actionable

Implementation Plan

ActionFileWhat changes
editapp/javascript/packs/application.js:7,12remove import Turbolinks / Turbolinks.start(), wire Turbo instead
editapp/views/layouts/application.html.erb:9-10, app/views/layouts/login.html.erb:10-12data-turbolinks-trackdata-turbo-track
editconfig/initializers/rails_admin.rb:12config.asset_source = :webpacker → the shakapacker-compatible value from Task 8

Implementation steps

  1. Remove the Turbolinks import/start calls in application.js; add turbo-rails's JS entry point.
  2. Update both layout ERBs' data-turbolinks-track attributes to data-turbo-track.
  3. Switch rails_admin.rb:12's asset_source to match whatever Task 8 lands (shakapacker), so the admin UI doesn't break independently of the main app.
  4. Manually regression-test navigation (page-transition behavior) across both the main app and rails_admin.

Acceptance criteria

  • turbolinks is removed from the app's own JS entry point; only turbo-rails remains.
  • Both layout ERBs use data-turbo-track.
  • rails_admin's asset_source matches the Task 8 pipeline; admin UI still renders correctly.

Test strategy: Manual navigation/page-transition regression pass on the main app and the admin UI; no existing automated FE test coverage for this path.

Effort estimate

DisciplineDays
Frontend2.5
QA1
Total3.5

Assumes turbo-rails's already-resolved version (2.0.16, pulled in via rails_admin) is sufficient — no separate gem bump needed, just wiring.

Run to verify

bin/shakapacker:compile && bundle exec rails server # manual navigation check

Depends on: Task 8 (asset pipeline must be shakapacker before rewiring asset_source). Coordinate with Task 7 (rails_admin spike).


Task 10: [BE/Infra] Final validation, staging bake, canary rollout (Track 2a)

Track 2a is validated end-to-end on Rails 8.1.3 and rolled out gradually across both deployment targets, closing the promptly-scheduled part of Phase 2.

Status: ✅ Actionable, gated on all prior Track 2a tasks.

Implementation Plan

ActionFileWhat changes
n/adeploy/, deploy-alicloud/staged canary, one target first, then the other

Implementation steps

  1. Full rspec suite on the complete Track 2a branch (Rails 8.1.3, load_defaults 8.1, shakapacker, turbo-rails); boot-smoke every process type.
  2. Staging soak: Karafka consumer, sidekiq-cron schedules, Datadog traces, confirmed Sidekiq-8 Redis connectivity, asset-pipeline visual/functional parity (main app + rails_admin), and the rails_admin-on-8.1 spike result (Task 7) recorded.
  3. Canary one deployment target, monitor, then the second.

Acceptance criteria

  • Full rspec suite green; every process type boots cleanly on the complete Track 2a branch.
  • Staging soak passes on all items in Step 2; canary succeeds on both AWS and Alicloud, staged.

Test strategy: Full rspec suite + manual staging soak checklist + a staged production canary with monitoring before full rollout.

Effort estimate

DisciplineDays
Backend/Infra2
QA1.5
Total3.5

Single point validating all 9 prior tasks' outputs together for the first time.

Run to verify

bundle exec rspec && bundle exec karafka console

Depends on: All prior tasks (1–9).


Ordering rationale

  • Track 2a follows the RFC's own numbered execution plan: Rails 8.0.5 bumps first (Task 1, compatible with Phase 1's acts_as_paranoid 0.10.3 ceiling), then the gem wave (Task 2) and Sidekiq 8 (Task 3) land on the 8.0 base, then defaults step to 8.0 (Task 4), then the 8.0 checkpoint (Task 5) gates the 8.1 bump (Task 6) — this is the "compress the gap, don't skip the checkpoint" structure from the RFC's direct-jump assessment.
  • The asset-pipeline workstream (Tasks 8–9) is the one item that should start early, in parallel with Phase 1's tail, despite appearing later in the RFC's own execution-plan numbering — it's independent of the pure Rails version bumps and is explicitly called out as the highest-effort, FE-touching item that shouldn't be squeezed into this phase's end.
  • Task 3 (Sidekiq 8) is ⚠️ partially blocked on Sidekiq Pro 8 registry/license access — push on that confirmation in parallel with Tasks 1–2 so it doesn't stall Task 4's defaults stepping.
  • Task 7 (rails_admin spike) and Tasks 8–9 (asset pipeline) are coupled via asset_source — sequence Task 7's final confirmation after Task 8 lands, not before.
  • Task 10 (final validation/canary) depends on everything — nothing in Track 2a skips its own per-step validation on the way there.

Skipped / deferred

Track 2b (Ruby 3.4.10 → 4.0.5) is fully deferred per the RFC's own "recommend DEFER" stance — Ruby 3.4 has runway to 2028-03-31, and Rails 8.1 must land before Ruby 4.0 is viable regardless of sequencing. No Track 2b items are actionable tasks in this breakdown; they are summarized below and revisited as their own bucket story around 2027-Q1 (RFC Open Question 5).

ItemStatusUnblocking condition
Spike: dry-rails, seed_migration, amoeba, gon, clean-architecture, aliyun-sdk, rest-client, wit, byebug against Ruby 4.0DeferredTrack 2b scheduling decision (RFC Open Question 4/5)
Re-run bundled-gem require grep for the 4.0-specific list (ostruct, pstore, benchmark, logger, rdoc, win32ole, irb)DeferredTrack 2b scheduling; note lib/http.rb:153,155,157's missing require 'ostruct' is a known latent hazard already — fix no later than Track 2b, earlier if Task 3/Phase-1's pigeon-http fallback makes lib/http.rb the WitAi::AppV2 replacement
Native-extension recompile surface (pg, nokogiri, google-protobuf, ffi, msgpack, sassc, karafka-rdkafka, bcrypt_pbkdf, ed25519, Datadog native libs)DeferredTrack 2b scheduling
Set core-class change vs. config/initializers/system.rb's clean-architecture/athens monkey-patchesDeferredTrack 2b scheduling
Ruby .ruby-version/Gemfile/Dockerfiles/CI image bump to 4.0.5DeferredTrack 2b scheduling
Full validation + staging bake + canary for Ruby 4.0.5DeferredTrack 2b scheduling

Note: Redis server ≥ 7.0 (Task 3 prerequisite) is not a deferred item — it is already met, stakeholder-confirmed 2026-07-13. The app's separate cache/app Redis (6.0, itself an EOL line) is also out of scope for both Phase 1 and Phase 2 — flagged as a candidate for its own future bucket story, not a Track 2a/2b task.