Skip to main content

RFC — Phase 1: Upgrade Ruby 3.2.2 → 3.4.10 and Rails 7.1.3.2 → 7.2.3.1

Bucket item: A1 · Class: 🔴 Expedite · Jira Story: BOT-4621 · Epic: BOT-4620

Context / Problem

The chatbot app's runtime and framework are both out of support:

  • Ruby 3.2 reached end-of-life on 2026-03-31 and receives no further security fixes. The app runs 3.2.2, which is additionally ~9 patch releases behind the last 3.2.x (3.2.11, 2026-03-27) — it has been unpatched since before the series even went EOL.
  • Rails 7.1 security support ended 2025-10-01 (7.1.6, 2025-10-29, was announced as the final 7.1 release). The app runs 7.1.3.2 — it has missed every security release from 7.1.3.3 through 7.1.6.

Missed security releases (Rails 7.1 track)

ReleaseDateIssueCVE status
7.1.3.3 / 7.1.3.42024-05–06Security releasesIDs not individually re-verified in research — UNVERIFIED detail
7.1.4.12024-10-154 possible ReDoS attacks (Action Dispatch / Action Mailer)CVE-2024-47887/47888/47889 family — exact ID mapping UNVERIFIED
7.1.5.12024-12-10XSS via content_security_policy helper with untrusted inputCVE-2024-54133 — UNVERIFIED (recalled from memory, not re-checked against NVD)
7.1.5.22025-08-13Two Active Storage security issuesCVE-2025-24293 family — partially verified
7.1.62025-10-29Final 7.1 release; Rails core announced no further security/bug fixes for the series
2026-03-23 patch day (7.2.3.1 / 8.0.4.1 / 8.1.2.1)2026-03-2310 security issues: XSS in Action Pack/Action View/Active Support (incl. debug-exceptions XSS), DoS via Active Storage range requests, Active Support number-helper DoS, path traversal/glob injection in Active Storage DiskService, insufficient direct-upload metadata filteringCVE-2026-33167 (debug-exceptions XSS) and CVE-2026-33176 (AS number-helper DoS) are the two IDs research.md confirmed; the remaining 8 issues in that batch are not individually ID-mapped here — UNVERIFIED count/IDs beyond these two

Rails 7.1.3.2 (past its own EOL since 2025-10-01) received no fix for the entire 2026-03-23 batch. This is the strongest concrete security argument for this RFC: every day on 7.1.3.2 is a day against ten confirmed unpatched framework-level issues, two of them with identified CVE numbers.

Current-state evidence (version pins and framework-defaults gap)

LocationCurrent valueNote
.ruby-version:1ruby-3.2.2
Gemfile:6ruby '3.2.2'
Gemfile.lock:939-940RUBY VERSION ruby 3.2.2p53
Dockerfile:1-2,21ruby:3.2.2-alpine (both build stages)
Dockerfile.nonroot:1-2,23,42ruby:3.2.2-alpine (both build stages)
Dockerfile.develop:1,52ruby:3.1.0-alpine (both stages)Pre-existing defect: one full Ruby minor behind the other 6 Ruby-version-bearing locations. Must be corrected in this RFC's execution, not left further behind.
bitbucket-pipelines.yml:1image: ruby:3.2.2
Gemfile.lock:942-943BUNDLED WITH 2.4.10
Dockerfile:32, Dockerfile.develop:6, Dockerfile.nonroot:42ENV BUNDLE_VERSION 2.3.8 (Dockerfile, Dockerfile.develop); ENV BUNDLE_VERSION=2.3.8 with = (Dockerfile.nonroot)
bitbucket-pipelines.yml:26,50,67,296,344,359 (bundlerCache, rspecSetup, code-coverage/rspec, vulnerabilitiesCheckDDMetric, rubocop/Lint, reviewdog blocks — 6 occurrences)gem install bundler -v 2.2.33-way Bundler mismatch: lock 2.4.10 / Dockerfiles 2.3.8 / CI 2.2.3 — must be reconciled to one version before the Ruby bump, since old Bundler can fail against newer RubyGems.
Gemfile:9gem 'rails', '~> 7.1.3.2'
config/application.rb:13config.load_defaults 6.1
config/initializers/new_framework_defaults_7_0.rb13 of 19 settings live/uncommented (e.g. active_record.partial_inserts, active_storage.variant_processor = :vips, action_controller.wrap_parameters_by_default)The app already runs a hand-applied hybrid of some Rails 7.0 defaults while load_defaults itself still says 6.1, with no record of why each setting was chosen.
config/initializers/new_framework_defaults_7_1.rb100% commented out (284 lines)Dead documentation — no 7.1 default has ever been applied.

7 Ruby-version-bearing locations exist in total (.ruby-version, Gemfile, Gemfile.lock, 3 Dockerfiles, 1 CI image); 6 agree at 3.2.2, Dockerfile.develop is stale at 3.1.0.

Target & sequencing decision

Rails 7.2.3.1 first (on Ruby 3.2.2), then Ruby 3.4.10.

  • Rails 7.2's gemspec requires Ruby ≥ 3.1.0, which the current 3.2.2 already satisfies — Rails can move first with zero Ruby change.
  • Ruby 3.4 is fine on Rails 7.2 (7.2.2+ ships the relevant compatibility fixes; community compatibility guidance lists 3.4 as recommended for 7.1/7.2/8.0/8.1), so Ruby moves second onto an already-current framework.
  • The rejected order (Ruby 3.4 first, on Rails 7.1.3.2) leaves the app on a Ruby version paired with a framework release that will never receive fixes for it — Rails 7.1 is past all support and was never patched for Ruby 3.4-era changes. Every hop in this plan keeps the other component in a version that supports it; only the Rails-first order does that throughout.

7.2 is a transit station, not a resting state. Rails 7.2's security support ends 2026-08-09 — under a month from today (2026-07-13). Landing here buys a supported base to upgrade from, not a stable long-term target. Phase 2 (Rails 8.1) must be scheduled immediately after this phase stabilizes — see phase-2-rails-8-1-ruby-4-0-upgrade.md, which treats the gap between phases as the thing to compress, not a reason to skip this phase.

Security advisory: this phase's own scope may outrun the 2026-08-09 date. The wave-0 gem sequence (in particular the sequential Karafka 2.2→2.5 chain and the ddtracedatadog migration), the 46-declaration enum modernization, and the three-step load_defaults stepping (7.0 → 7.1 → 7.2) are realistically weeks of sequenced, individually-validated deploys — it is plausible the app lands on Rails 7.2 already at or near its own security-support cliff rather than comfortably inside it. This is not a reason to pick a farther endpoint: Rails 7.2 (not 8.0.5) is still the correct Phase-1 target because it is the only single-minor hop from 7.1 that preserves the deprecation-warning safety net the upgrade guide relies on (see the Phase 2 RFC's direct-jump assessment) — moving the Phase-1 endpoint to 8.0.5 would simply relocate the skip-a-minor risk this two-phase structure exists to avoid. The correct mitigation is compressing the Phase 1 → Phase 2 gap, not choosing a farther Phase-1 target; see the Phase 2 RFC's "big-bang vs. phased with compressed gap" comparison.

Dependency blockers & gem waves

Gem (current)TargetWhy / wave
acts_as_paranoid 0.9.00.10.3Hard Rails-7.2 resolution blocker: Gemfile.lock:105-107 pins activerecord/activesupport >= 6.1, < 7.2. Bundler cannot resolve Rails 7.2.3.1 until this ceiling is lifted. 0.10.3 allows AR/AS >= 6.1, < 8.1. Wave 0 (bump on current Ruby 3.2.2/Rails 7.1.3.2 stack, separate small deploy, before the Rails PR).
ddtrace 1.19.0Migrate to datadog 2.x (e.g. 2.37.0)ddtrace 1.x supports MRI max Ruby 3.3 — a hard blocker before the Ruby 3.4 bump. Also carries debase-ruby_core_source (= 3.3.1) (native, vendors Ruby's C source tied to a specific minor — must be re-pinned to match whichever Ruby patch is deployed) and libdatadog/libddwaf native binaries. Migration is a real API change (Datadog.configure namespacing, profiler flags), not a version bump — budget it as its own wave-0 deploy, done well before the Ruby PR.
google-protobuf ~>3.19 (3.25.2)Bump to a Ruby-3.4-compatible release, then remove the pin3.25.x fails to install on Ruby 3.4 (upstream issue). It was only added to support Datadog profiling; once migrated to datadog 2.x (which uses libdatadog, not protobuf), the pin can likely be deleted entirely. Sequence with the ddtrace migration.
pigeon-http 0.1.5 (private gem)Preferred: owner re-releases dropping the unused ddtrace gemspec dependency (one-line fix). Decided fallback (2026-07-13): remove pigeon-http entirely.Source-audited 2026-07-13 against the pinned v0.1.5 tag content (owner's clone HEAD is 10 non-merge commits ahead, 11 total incl. merge, touching lib/pigeon/core.rb/lib/pigeon/http.rb — not yet released, so not relied on here). pigeon.gemspec:38 declares an unconstrained ddtrace dependency that pigeon-http's own lib/ code never requires or calls anywhere — it only uses datadog/statsd via the separate dogstatsd-ruby gem, so the ddtrace dep is dead weight. Today's resolved ddtrace 1.19.0 is governed entirely by chatbot's own Gemfile:200 pin (~> 1.12, >= 1.12.1), not by pigeon-http. It nonetheless remains a Ruby-3.4 blocker at bundle-resolution time: even after chatbot migrates to datadog 2.x, pigeon-http's unconstrained dependency still forces Bundler to resolve some ddtrace, whose 1.x line caps at Ruby 3.3. The fix is verified-trivial (a gemspec-only change), but if the owning team can't turn it around in time, the decided fallback is removal: blast radius verified small — exactly one wrapper (lib/pigeon_http.rb) and one consumer (lib/wit_ai/app_v2.rb, 5 call sites: create/get_all/get/update/delete against Wit.ai); circuit-breaker/retry options are not actually exercised in production (already known-broken/unused per the wrapper's own code comment); zero existing test coverage to preserve; and the in-house lib/http.rb wrapper (already used by 5 other service clients, near-identical Entities::APIResponse response shape) is the natural drop-in replacement. Wave 0 either way.
sidekiq 7.2.1 (+ sidekiq-pro 7.2.1)sidekiq 7.3.107.3.6+ ships forward-compatibility fixes for Ruby 3.4. sidekiq-pro's existing sidekiq < 8 constraint is already satisfied — stays 7.x this phase. Wave 0.
karafka 2.2.14 + karafka-web 0.7.10karafka 2.5.x, karafka-web 0.11.xKarafka is already past its own EOL at 2.2 and mandates sequential minor upgrades (2.2→2.3→2.4→2.5, each with its own upstream upgrade doc) plus karafka-rdkafka native-ext rebuilds at each step. Wave 0, but treat as its own multi-step sub-sequence, not one PR.
rack-cors 1.1.12.x (e.g. 2.0.2)1.x is not Rack-3-safe (lowercased header handling) — already a latent bug today since the app runs Rack 3.0.9.1 (confirmed resolved version, audit §3). Wave 0.
grape 2.0.02.4.xBump for Rack 3 / Ruby 3.4 currency (2.1–2.4 include Rack 3.x fixes) per research.md. grape-swagger and grape-entity stay at their currently-resolved versions this wave — no compatibility issue flagged for them at Rails 7.2/Ruby 3.4. Wave 0.
webdrivers 5.3.1RemoveGem is archived/unmaintained and pins selenium-webdriver ~> 4.0, < 4.11, which just barely still resolves against the app's current selenium-webdriver 4.10.0 — any future selenium bump breaks it. Replace with Selenium Manager, built into selenium-webdriver >= 4.11. Test/dev-only; wave 0.
rubocop 1.59.0 / rubocop-performance ~1.9.2 / parser (transitive, ~3.2.x)rubocop ≥ 1.88, drop the 1.9.2 performance pinparser 3.2.x cannot parse Ruby 3.4 syntax — the lint toolchain must move before/alongside the Ruby bump so CI can actually run. Also fix the stale .rubocop.yml:6 TargetRubyVersion: 2.6.3 (4 majors behind; the file's own comment at line 5 explains it was set low as a workaround, never revisited) to 3.4. No TargetRailsVersion key exists in the file — none to fix.
webpacker 5.4.4No forced bump — add a boots-on-7.2 CI gateConfirmed load-bearing, not vestigial (real webpacker:compile in both build Dockerfiles; live Turbolinks in app/javascript/packs/application.js). Unmaintained since 2021; whether it still boots/compiles cleanly under Rails 7.2 is unverified from this repo. Full migration to shakapacker is deferred to Phase 2 (FE-touching); Phase 1 only needs the CI asset-compile gate (see Execution plan) plus a documented fallback (pin a known-compatible sprockets/webpacker combination) if the gate fails.

Wave-0 principle: every gem above except the framework version itself can and should land as a separate small deploy on the current stack (Ruby 3.2.2 / Rails 7.1.3.2) before the Rails 7.2 bump PR. This shrinks the risky change to "flip the Rails/Ruby version with dependencies already proven compatible" instead of "flip the version and fix N gems simultaneously."

Ruby 3.2 → 3.4 language impact

  • Chilled string literals: files without # frozen_string_literal: now compile string literals as "chilled"; mutation only warns when Warning[:deprecated] = true is explicitly enabled (not on by default) — no forced behavior change, but worth enabling in CI to surface latent mutation bugs before Phase 2 (where the default may tighten further).
  • it implicit block parameter: a bare it call inside a block with no explicit parameter now resolves to the block's first argument. Audit grepped app//lib/ for bare it usage as a block-local and found zero hits — clean.
  • Prism becomes the default parser. Tools pinned to the old parser gem cannot parse Ruby 3.4 syntax — this is why the rubocop/parser bump above must land before or with the Ruby PR, not after.
  • Default gems become bundled gems in 3.4: abbrev, base64, bigdecimal, csv, drb, getoptlong, mutex_m, nkf, observer, repl_type_completor, resolv-replace, rinda, syslog. Any of these required directly by app code without a Gemfile entry will raise LoadError under Bundler once no longer shipped by default.
    • require 'base64' — 3 hits: lib/auth/token_flows/oauth2_client_credentials.rb:3, lib/auth/header_strategies/basic_auth.rb:3, lib/mekari_hmac/signer.rb:4. base64 is already an explicit transitive dependency of activesupport/sidekiq-pro per the lock, so no LoadError is expected — confirm it stays declared after the bump.
    • require of csv/mutex_m/drb/observer/getoptlong/rinda/syslog/abbrevzero direct hits in app code (all pulled transitively via activesupport/rails_admin/sshkit). Clean at the app-code level, but re-run this grep after the bump as a verification step, not just before.
    • remote_syslog_logger 1.0.4 is understood to be pure-Ruby over the syslog_protocol gem, not the stdlib syslog — this was not independently code-verified (research.md flags it as such); grep the gem's own source or its dependency tree before assuming it is unaffected.
  • OpenStruct — ~24 hits across lib/http.rb, lib/qontak_crm_service/http.rb, lib/harmonia/http.rb, lib/mekari_account/http.rb, lib/hub/chat_service/http.rb, and several app/core/{repositories,use_cases}//app/api/ files. ostruct is not removed from default gems in 3.4 (that happens in 4.0 — see the Phase 2 RFC), but confirm it stays resolvable and watch for new deprecation noise.

Rails 7.1 → 7.2 framework impact

  • ActiveSupport::Deprecation singleton delegation is removed in 7.2 — callers must use Rails.application.deprecators or an owned Deprecation instance. Audit grepped app/ lib/ config/ for direct ActiveSupport::Deprecation. calls and found zero hits — clean, no code will break on this removal.
  • Enum-syntax modernization — the largest volume of app-code surface for this bump. Audit found 46 legacy hash-argument enum declarations across app/models/*.rb (e.g. app/models/intent.rb:73-76, app/models/path.rb:58,63, app/models/response.rb:53-55, app/models/attachment.rb:52-53). Rails 7.0+ introduced the positional enum :col, {...}, suffix: true form and has progressively tightened the legacy hash-argument form across 7.0→7.2. Every one of the 46 call sites must be checked against the exact Rails 7.2 enum changelog before this bump lands — mechanically fixable, but non-trivial surface area, and a missed case fails silently as a wrong enum mapping, not a boot error.
  • Staged load_defaults plan — do not jump straight from 6.1 to 7.2:
    1. Land Rails 7.2.3.1 with config.load_defaults 6.1 unchanged and the existing hand-applied 7.0-hybrid initializer untouched. This isolates "does the app boot on the new framework version" from "does it behave under new defaults."
    2. Step to new_framework_defaults_7_0.rb fully uncommented (all settings, not just the 13 of 19 already live), deploy, monitor, then flip load_defaults to 7.0. Dangerous flags at this step: the SHA-256 key generator (affects encrypted cookies and message verifiers — rotating this changes both), cache_format_version moving to the 7.0 format, and cookies_serializer interaction (already :json per config/initializers/cookies_serializer.rb:5, so lower risk here than a fresh app).
    3. Step to new_framework_defaults_7_1.rb fully uncommented (currently 100% commented, 284 lines — every setting is templated but unapplied), deploy, monitor, flip load_defaults to 7.1. Dangerous flags: to_time_preserves_timezone, the Active Support message-serializer default move, and the cache serializer default. Confirm as part of this step that the active_record.encryption.hash_digest_class change is a no-op for this app: grep found no Rails-native encrypts usage anywhere in app//lib/ — this app's encryption is via lockbox, not Active Record Encryption, so this particular 7.1 default has nothing to act on.
    4. Generate new_framework_defaults_7_2.rb via bin/rails app:update, review its (not exhaustively published in the release notes) settings, uncomment, deploy, flip load_defaults to 7.2.
    5. Rolling-deploy note: each of steps 2–4 runs mixed-version pods during a rolling deploy. Cache-format and cookie-serializer changes are the specific risk — a pod on the new defaults writing a cache entry or cookie that an old-defaults pod cannot read (or vice versa) causes transient cache misses / cookie invalidation during the rollout window, not a hard failure. Plan the rollout window and cache-flush expectations accordingly, and keep each defaults step as an independent, individually-revertible deploy.

Execution plan (call to action)

  1. Bundler reconciliation. Pick one target Bundler version compatible with Ruby 3.4 (e.g. 2.5.x+). Update Gemfile.lock's BUNDLED WITH, ENV BUNDLE_VERSION in all three Dockerfiles (Dockerfile:32, Dockerfile.develop:6, Dockerfile.nonroot:42 — the last uses ENV BUNDLE_VERSION=2.3.8 with =), and all 6 gem install bundler -v 2.2.3 occurrences in bitbucket-pipelines.yml (lines 26 bundlerCache, 50 rspecSetup, 67 code-coverage/rspec, 296 vulnerabilitiesCheckDDMetric, 344 rubocop/Lint, 359 reviewdog).
  2. Wave-0 gem deploys on the current stack (Ruby 3.2.2 / Rails 7.1.3.2), each as its own small PR/deploy: acts_as_paranoid → 0.10.3; ddtracedatadog 2.x migration (preferred: the pigeon-http owner re-releases it dropping the unused ddtrace gemspec dependency, a one-line gemspec change, before this lands; decided fallback: remove pigeon-http entirely and port its 5 lib/wit_ai/app_v2.rb call sites to the in-house lib/http.rb wrapper); sidekiq → 7.3.10; karafka 2.2→2.3→2.4→2.5 sequential + karafka-web → 0.11; rack-cors → 2.x; grape → 2.4.x; remove webdrivers, bump selenium-webdriver; bump rubocop/drop rubocop-performance pin, fix .rubocop.yml:6 TargetRubyVersion to 3.4.
  3. CI additions before the framework bump: add an asset-precompile step (bundle exec rake webpacker:compile && bundle exec rake assets:precompile) to bitbucket-pipelines.yml (currently absent — audit §5); add a Karafka boot smoke step (currently zero coverage — audit §6); add explicit boot checks for every process type (Puma, Sidekiq, Karafka, rails console, the sidekiq-cron schedule load, and update_automation_result.rb's bare-ruby invocation — confirm whether it loads the Rails environment at all, which is currently unverified).
  4. Rails 7.2 bump PR: bump Gemfile:9 to gem 'rails', '~> 7.2.3.1', run bundle update rails (only after step 2's acts_as_paranoid bump has landed, or resolution fails), run bin/rails app:update and review every generated/changed file as an explicit diff (do not blanket-accept), and re-verify the 46 enum declarations against the 7.2 changelog.
  5. load_defaults stepping deploys — 7.0 → 7.1 → 7.2, each its own deploy, per the staged plan above.
  6. Ruby 3.4.10 bump PR (only after wave-0's ddtracedatadog migration is complete, since ddtrace 1.x cannot run on Ruby 3.4): update .ruby-version, Gemfile:6, all three Dockerfiles (fixing Dockerfile.develop's stale 3.1.0 pin in the same PR), bitbucket-pipelines.yml:1's image, and re-pin debase-ruby_core_source to match Ruby 3.4's ABI if any native profiling dependency still requires it post-migration.
  7. Staging bake + canary: full staging soak (see Validation) before a production canary rollout; roll out gradually across both deployment targets (AWS and Alicloud — confirmed dual-cloud in audit §10) rather than simultaneously.
  8. Per-step rollback: each wave-0 gem PR, each load_defaults step, and the Rails/Ruby bump PRs are independently revertible; see Risks & rollback below for the exceptions.

Validation

  • Full bundle exec rspec suite (all ~11 sharded CI steps) green at each stage.
  • Boot smoke test per process type: Puma, Sidekiq worker, Sidekiq-cron scheduler, Karafka consumer, rails console, DB migration job.
  • Staging soak covering: Karafka consumer processing real chatbot_incoming_message traffic, sidekiq-cron schedules firing (config/schedule.yml, loaded at config/initializers/sidekiq.rb:46), and Datadog traces/APM flowing correctly after the datadog 2.x migration.
  • Vault-unreachable boot behavior. config/application.rb:41-79 fetches Vault secrets synchronously at boot with a bare rescue [] that swallows all exceptions (network, auth, timeout) — a fail-open path where the app can boot with missing credentials and no visible error. This is a pre-existing issue, not introduced by this upgrade, but the new Ruby/Rails boot path changes process boot order/timing enough to warrant re-asserting the expected behavior explicitly (does every process type still boot the same way when Vault is unreachable, and is that silence actually acceptable) rather than letting the upgrade silently carry the same gap forward unexamined.
  • Asset-compile parity: confirm webpacker:compile and assets:precompile produce a bootable, visually-correct app under Rails 7.2 (new CI gate from Execution plan step 3).
  • aegis boot/logging check under Rails 7.2: confirm Aegis::Logger's #tagged duck-typing status against config.log_tags = [:request_id] (does it still silently no-op, or start raising), and confirm the Sidekiq client/server middleware chain still wraps jobs correctly at the target Sidekiq version.

Risks & rollback

  • Per-wave rollback: each wave-0 gem bump and each Dockerfile/CI change reverts independently (revert the image tag or gem-version PR); no cross-wave coupling by design.
  • Irreversibility call-outs: the load_defaults cache-format and cookie-serializer steps are not cleanly revertible mid-rollout — reverting after cache/cookie formats have already changed on some pods requires an explicit rollback flag (e.g. temporarily forcing the old cache/cookie format) rather than a plain code revert, to avoid stranding sessions/cache entries written under the new format.
  • aegis private gem (git@bitbucket.org:terbang-ventures/aegis.git, pinned tag 0.2.12) — source-audited 2026-07-13 against the pinned tag content (the owner's clone HEAD is 5 commits ahead, touching lib/aegis/logger.rb/lib/aegis/fingerprint.rb — this is the candidate "updated aegis" path if a newer release is later needed). Assessed compatible-as-is for Phase 1 (medium-high confidence): required_ruby_version = '>= 2.6.0' has no upper ceiling, and none of aegis's 15 lib files touch any Ruby-3.4 default-gem departure, OpenStruct, or other hazard pattern. Two residual verification items remain, neither resolvable from static reading alone: (i) Rails 7.2's ActiveSupport::BroadcastLogger interaction with Aegis.replace_rails_logger! (config/initializers/aegis.rb:21) — the swap installs a plain ::Logger subclass with no #tagged method, which already silently defeats config.log_tags = [:request_id] (config/environments/production.rb:71) today at Rails 7.1.3.2 — a pre-existing defect, not introduced by this upgrade, but worth fixing the next time aegis is updated; (ii) Sidekiq 7.3+ middleware-chain API drift against aegis's client/server patch classes. Two further audit findings change the risk picture: aegis has no CI at all (confirmed absent across its entire git history), and its gemspec omits opentelemetry-api/opentelemetry-sdk even though 4 lib files unconditionally require them — masked today only because chatbot's own Gemfile:230,232 already declares both directly, so those two Gemfile lines must not be removed as "unused" during this upgrade. Decided fallback (2026-07-13): temporarily detach aegis if it blocks — verified observability-only impact (loses structured JSON logs, PII masking, error fingerprinting/grouping, trace correlation, and Sidekiq job tracing; no functional HTTP/Sidekiq/DB behavior depends on it). Caveat: PII masking is a data-protection control, not pure observability — if aegis is detached, explicitly confirm no other log path (lograge, Rails' default logger, config/initializers/filter_parameter_logging.rb) emits unmasked PII in the interim; do not assume "observability-only" covers this specific loss without that check.
  • webpacker boot risk: if the new CI gate (step 3) fails against Rails 7.2, the documented contingency is to pin a known-compatible sprockets/webpacker version combination rather than attempt an emergency shakapacker migration mid-Phase-1 — that migration is explicitly Phase 2 scope.

Open questions

  1. Who owns the pigeon-http private-gem re-release against datadog 2.x, and on what timeline? Resolved 2026-07-13: source-audited (see the gem-waves table above). Preferred fix is a one-line gemspec change by the owner (drop the unused, unconstrained ddtrace dep); decided fallback if that doesn't land in time is removing pigeon-http entirely — blast radius verified small.
  2. Does aegis 0.2.12 function correctly on Ruby 3.4/Rails 7.2? Needs a spike against its actual source, not inference from this repo. Resolved 2026-07-13: source-audited against the pinned tag (see Risks & rollback above) — assessed compatible-as-is (medium-high confidence), with two residual verification items (Rails 7.2 BroadcastLogger/#tagged interaction, Sidekiq 7.3+ middleware API) that still require booting a real upgrade branch to close. Decided fallback if it blocks: temporarily detach aegis (verified observability-only loss — except PII masking; see the data-protection caveat in Risks & rollback).
  3. What Redis server version runs in production? Sidekiq 7.3 needs Redis server ≥ 6.0/6.2 — unverified from this audit (only the redis gem client version was confirmed). Resolved 2026-07-13 (stakeholder-confirmed): the Redis server backing Sidekiq is 7.x — the ≥ 6.0/6.2 requirement is satisfied. The app's other Redis (cache/app usage) is 6.0, which nothing in Phase 1 requires upgrading.
  4. Does the team's sidekiq-pro registry access (gems.contribsys.com) cover the 7.3.10 release, or does it need a subscription check?
  5. Does Datadog profiling parity hold after the ddtracedatadog 2.x migration (native libdatadog/libddwaf behavior vs. the old debase-ruby_core_source approach)?
  6. Does webpacker 5.4.4 actually boot and compile cleanly under Rails 7.2 — no authoritative compatibility statement exists; must be answered by the new CI gate, not assumed.