All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
1.6.0 - 2026-06-08
- i18n support
- Custom dashboard cards —
config.dashboard_cardsaccepts an array of{ title:, stats:, link: }hashes; each card is rendered after the built-in queue stat cards on the dashboard;stats:is a lambda returning a{ label => value }hash evaluated at render time;link:is an optional{ label:, url: }header link - Custom nav links —
config.nav_linksaccepts an array of{ label:, url: }hashes appended to the main navigation bar after the built-in links
- README restructured with a Table of Contents, section dividers, and "Back to top" links; alert sections consolidated under a single
## Webhook alertsparent — all UI strings are now extracted intoconfig/locales/en.yml; locale switching via?locale=param or session is supported; host apps can add additional locales usingSolidQueueWeb.available_locales = %i[en es]; the English locale file can be used as a template for translations
1.5.0 - 2026-05-28
- Admin audit log — every discard, retry, pause, and resume action is recorded to a
solid_queue_web_audit_eventstable; viewable at/jobs/auditwith action/actor/queue filters and CSV export; identity captured via the optionalSolidQueueWeb.current_actorconfig block; table created viarails generate solid_queue_web:install:migrations
1.4.0 - 2026-05-28
- Slow job webhook alert — set
alert_slow_job_count_threshold(integer) to fire a webhook whenever the number of currently-running slow jobs meets or exceeds the configured count; requiresslow_job_thresholdto define what "slow" means; uses the samealert_webhook_urlandalert_webhook_cooldownsettings as other alert types; event nameslow_job_threshold_exceeded - Stale process webhook alert — set
alert_stale_process_threshold(integer) to fire a webhook whenever the number of stale workers meets or exceeds the configured count; a process is stale when its heartbeat has not been updated withinSolidQueue.process_alive_threshold; uses the samealert_webhook_urlandalert_webhook_cooldownsettings; event namestale_process_detected - Job wait time column — the Running tab now shows a "Wait Time" column displaying how long each claimed job waited in the queue from enqueue to pickup; also included as
wait_time_secondsin the CSV export for the claimed status - Eliminate N+1 queries on the queues index —
queue.sizeandqueue.paused?previously fired one query each per queue; both are now pre-computed in the controller with single batch aggregations (GROUP BY queue_namefor sizes, onePausequery for paused state), reducing query count from O(2N+constant) to O(constant) regardless of queue count
1.3.0 - 2026-05-27
-
Sticky filter preferences — last-used status tab (Jobs) and time-period pill (Jobs, Failed Jobs, History) are persisted to
localStorage; revisiting a page via the nav link restores the previous filter automatically; clicking "All" clears the saved period; implemented as a lightweight Stimulus controller (FiltersController) with no server-side changes -
Configurable display timezone —
SolidQueueWeb.configure { |c| c.time_zone = "America/New_York" }renders all dashboard timestamps in the configured zone rather than UTC; usesin_time_zonefrom ActiveSupport; defaults to UTC when not set; aformat_timestamphelper centralises all timestamp formatting across jobs, failed jobs, history, search, queues, recurring tasks, and the scheduled-job turbo stream update -
Sortable table columns — Jobs, Failed Jobs, and History tables now support server-side sorting via
?sort=and?direction=params; click any column header to sort ascending or descending; sort state is preserved across filter changes, status tab switches, and period buttons; asort_header_thhelper generates accessible<th>elements witharia-sortand direction indicators (↑/↓)
1.2.0 - 2026-05-27
-
p99 and standard deviation columns in performance analytics —
JobPerformanceStatsnow computes a 99th percentile and population standard deviation for each job class; both columns appear in the Performance table between p95 and Min; high std dev surfaces inconsistent jobs worth investigating -
Failed job trend chart — a "Failures — Last 12 Hours" bar chart card on the dashboard shows how many jobs failed per hour over the last 12 hours; bars are red (distinct from the blue throughput and purple queue depth charts); header shows the total failure count for the window; empty state shown when no failures exist in the period
-
Error frequency report — a new Error Summary page (
/jobs/failed_jobs/errors) groups all failed jobs by error class and message prefix, shows a count per group, and displays a sample backtrace (first 10 lines) in an expandable<details>element; groups are sorted by count descending so the most common errors appear first; accessible via an "Error Summary" button on the Failed Jobs page
1.1.0 - 2026-05-21
- Queue depth alert —
alert_queue_thresholdsaccepts a hash ofqueue_name => ready_job_count; a webhook fires when any configured queue's ready count meets or exceeds its threshold; cooldown is tracked independently per queue so a busy queue doesn't suppress alerts for others; uses the samealert_webhook_urlendpoint(s) withevent: "queue_depth_threshold_exceeded"andqueue_name,depth, andthresholdfields in the payload - Multiple webhook targets —
alert_webhook_urlnow accepts an array of URL strings; all configured endpoints receive the same payload when the failure threshold is exceeded; a failure posting to one URL is logged and skipped without blocking the remaining targets - Retry failed job with modified arguments — the Arguments card on the job detail page becomes an editable textarea for failed jobs; editing the JSON and clicking "Retry with these arguments" updates the job record and retries in one step; invalid JSON redirects back with an error message without touching the failed execution
1.0.0 - 2026-05-21
- Bulk scheduled job actions — a "Run All Now" button on the Scheduled tab back-dates all scheduled executions in a single
update_allcall, causing SolidQueue's dispatcher to pick them up immediately; respects the active period filter so only jobs within the current window are promoted - Priority filter — a
?priority=Nparam on the jobs index narrows the list to a specific integer priority value; a select dropdown appears in the search bar when multiple distinct priorities exist in the current status; priority is preserved across status tab switches, period changes, and search; Discard All also respects the active priority filter - Performance analytics — a new Performance page (
/jobs/performance) shows per-job-class statistics derived from the history table: run count, average duration, p50, p95, min, and max; rows are sorted by p95 descending so the slowest classes appear first; a period filter (1h / 24h / 7d / All) scopes the dataset; each class name links to the History page pre-filtered to that class; business logic lives in aJobPerformanceStatsservice using a single pluck query with Ruby-side aggregation for DB-agnostic percentile computation - Metrics / health endpoint —
GET /jobs/metrics.jsonreturns a JSON document with job counts (ready,scheduled,claimed,blocked,failed), throughput (completed_1h,completed_24h), per-queue depth and pause state, and process health (total,healthy,stale,by_kind); whenslow_job_thresholdis configured, aslow_jobscount is also included; the endpoint goes through the same authentication andconnects_tomiddleware as all other routes - Recurring task "Run Now" — a "Run Now" button on the Recurring Tasks page triggers
task.enqueue(at: Time.current)to enqueue the job immediately without waiting for its next scheduled run; SolidQueue'sRecurringExecutiondeduplication prevents double-enqueuing - Read replica support — when
connects_tois set to{ reading: <role>, writing: <role> }, the engine automatically routes GET requests to the reading role and mutating requests (POST/DELETE/PATCH) to the writing role viaActiveRecord::Base.connected_to(role:); passing any other hash (e.g.{ role: :writing },{ shard: :name }) falls through toconnected_todirectly; defaults tonilso single-database setups are unaffected - Webhook alert config —
alert_webhook_urlandalert_failure_thresholdsettings POST a JSON payload (event,failure_count,threshold,fired_at) to any URL when the failed job count meets or exceeds the threshold; fires asynchronously in a background thread so dashboard requests are never blocked; a configurablealert_webhook_cooldown(default 3600 s) prevents repeated alerts while the count stays elevated; HTTP errors are logged and swallowed - Bulk retry with delay — "+5s", "+10s", "+30s", and "+1m" stagger buttons on the Failed Jobs page retry all matched jobs with a configurable interval between each; the first job runs immediately, subsequent jobs are scheduled at incremental offsets; uses per-execution
retrysoscheduled_atis respected by SolidQueue's dispatcher; buttons only appear when more than one job is present - Scheduled job management — "Run Now" promotes a scheduled job to run immediately by back-dating its
scheduled_at; "+1h", "+24h", and "+7d" buttons pushscheduled_atforward by the chosen offset; both actions update the execution and the underlying job record; Turbo Stream responses remove the row on "Run Now" and update thescheduled_atcell in place on postpone
0.9.0 - 2026-05-20
- Failure rate sparkline per queue — a mini 12-bar chart in the Queues table shows the percentage of jobs that failed (vs. completed) in each of the last 12 hours; bars are red, sized proportionally to the failure rate (0–100 %), and include a tooltip with the hour label and exact percentage; empty hours render as a faint border-colored bar; queues with no activity in the last 12 hours show "—"
- Queue depth trend — a "Queue Depth — Last 12 Hours" card on the dashboard shows estimated queue depth at 12 hourly snapshots; depth at time T is the count of jobs created before T that had not yet finished by T; bars are purple (distinct from the blue throughput and red failure rate charts); the card header shows current depth; empty state shown when no active jobs exist in the window
- Slow job detection — a configurable
slow_job_thresholdsetting (default nil = disabled) flags claimed jobs that have been running longer than the threshold; when set, the Running tab gains a "Running For" column showing each job's elapsed time, slow jobs are highlighted with an orange row background and a "slow" badge, and a "Slow Jobs" warning card appears on the dashboard linking to the Running tab
QueuesController#pause/#resumeextracted intoQueues::PausesController#create/#destroy; pause maps toPOST /queues/:name/pause, resume toDELETE /queues/:name/pauseQueues::JobsController#discard_allmerged into#destroybranching onparams[:id], matching the pattern already used inJobsController
0.8.0 - 2026-05-20
- CSV export on the jobs, failed jobs, and history pages — an "Export CSV" button downloads all records matching the current filters (status, queue, search term, period) as a named
.csvfile; columns are tailored per view (enqueued_at for jobs, error details for failed jobs, duration and finished_at for history) - Dashboard quick actions — "Retry All Failed" and "Discard All Blocked" buttons appear as cards on the dashboard when the respective count is non-zero; each includes a confirm dialog and redirects back to the dashboard with a count notice; cards are hidden when everything is healthy
- Dark mode — a ☽/☀ toggle button in the header switches between light and dark themes; preference persists to
localStorageand falls back to the OSprefers-color-schemeon first visit; implemented via a[data-theme="dark"]attribute on<html>so all CSS custom properties inherit the new palette automatically; badge and flash hardcoded hex colors get explicit dark-mode overrides in a new_12_dark_mode.csspartial; powered by a new Stimulustheme_controller - Configurable settings via
SolidQueueWeb.configure—page_size(Pagy limit, default 25),dashboard_refresh_interval(ms, default 5 000),default_refresh_interval(ms, default 10 000 — applies to jobs, processes, and history pages),search_results_limit(max results per status in global search, default 25); all settings have safe defaults so zero host-app configuration is required
FailedJobsController#destroyand#discard_allconsolidated into a singledestroyaction branching onparams[:id];discard_allroute maps todestroyviaaction:optionFailedJobsController#retryand#retry_allextracted into a newRetryFailedJobsController#createaction with the same single/bulk branching patternJobsController#destroyand#discard_allconsolidated the same way, preserving the Turbo Stream response for the single-job path
0.7.0 - 2026-05-20
- Queue latency detail — oldest ready job age per queue; replaces the verbose
human_latencystring withformat_durationoutput (e.g. "1h 12m"); color-coded orange above 1 hour, red above 24 hours; hovering shows the absolute UTC timestamp of the oldest waiting job; empty queues show "—" - Queue throughput columns — Done (24h) and Failed (24h) counts per queue on the Queues page; powered by two grouped COUNT queries; Done renders in green, Failed renders in red when non-zero
- Auto-refresh on the Job History page — wraps the page in a turbo-frame polled every 10 seconds, matching the jobs and processes pages; pauses when the browser tab is hidden
- Job History page (
/jobs/history) — browsable list of all finished jobs (finished_at IS NOT NULL) ordered by most-recent-first; columns show class name (links to job detail), queue (clickable queue filter), duration (formatted asXs,Xm Xs, orXh Xm), and finished timestamp; filterable by time period (1h / 24h / 7d), queue name, and class name search; paginated; "Done (1h)" and "Done (24h)" dashboard stat cards now link to the history page pre-filtered by period; "History" nav link added between Jobs and Failed - Throughput section on the dashboard — "Done (1h)" and "Done (24h)" stat cards show completed-job counts; a full-width "Throughput — Last 12 Hours" card displays a pure-CSS bar chart (12 hourly buckets, oldest left → newest right, tick labels every 3 bars) with no JavaScript or charting library; powered by a single
pluck(:finished_at)query with Ruby-side grouping for DB-agnostic compatibility; seed data updated with a realistic daily-pattern distribution so the chart shows meaningful data out of the box
- CSS split into 10 focused partial files (
_01_base.cssthrough_10_responsive.css);inline_styleshelper globs_*.cssfiles in sort order — runtime output is identical, authoring is easier
- Auto-refresh no longer wipes checkbox selections — refresh skips its tick whenever any checkbox inside the turbo-frame is checked and resumes once selections are cleared or submitted
0.6.0 - 2026-05-19
- Job selection and targeted bulk actions — checkboxes on the jobs index (ready/scheduled/blocked) and failed jobs index; a selection bar appears above the table showing the count and action buttons ("Discard Selected" for jobs, "Retry Selected" / "Discard Selected" for failed jobs); select-all checkbox in the table header; powered by a new
selectionStimulus controller that injects checked IDs into a hidden form on submit; uses nested singular resources (Jobs::SelectionsController,FailedJobs::SelectionsController) following Rails conventions - Auto-refresh for dashboard, jobs list, and processes — a
refreshStimulus controller polls the current page at a configurable interval (5 s for dashboard, 10 s for jobs/processes) and swaps the matching<turbo-frame>content in place; polling pauses when the browser tab is hidden and resumes with an immediate refresh when the tab becomes visible again - Time-based period filter (
?period=1h|24h|7d) on the jobs and failed jobs indexes — filters results by enqueued timestamp; period pills render inline with the search bar, right-justified; active period is preserved across status tab switches, search, queue filters, and bulk actions - Global job search at
/jobs/search— queries all execution models (ready, scheduled, claimed, blocked, failed) by class name substring; results grouped by status with match count and a "View all →" link; native<datalist>autocomplete pre-populated from all known job class names; auto-submits on datalist selection via thesearchStimulus controller spec/dummy/bin/rails— enablesbin/rails consoleandbin/rails serverfrom the dummy app directory during local development
0.5.5 - 2026-05-19
- Failed jobs search:
?q=param filters by class name substring on the failed jobs index - Failed jobs queue filter: queue names in the failed jobs table are clickable links that filter to that queue; a "Filtering by queue" indicator appears with a clear link
- Queue-scoped Retry All / Discard All on failed jobs: bulk actions apply only to the active queue/search filter and redirect back preserving those params
- Blocked job detail: job show page displays "Blocked Until" (
BlockedExecution#expires_at) when the job has a blocked execution - Queue-scoped jobs view at
/jobs/queues/:queue_name/list— dedicatedQueues::JobsControllerwith status tabs, class name search, per-row discard (Turbo Stream), and Discard All scoped to the queue; navigated to by clicking queue names on the global jobs list
JobsController#indexno longer filters by queue — queue filtering is owned byQueues::JobsControllerJobsController#showno longer assigns@failed_execution/@blocked_execution; view reads associations directly from@job(already eager-loaded)- Removed stale
queue: @queuefrom status tab links in the jobs index (param was alwaysnilafter the queue controller refactor)
- Job detail links inside the turbo frame on the jobs index rendered "Content missing" because the show page has no matching frame; fixed with
data-turbo-frame="_top"so navigation breaks out to a full page load
0.5.0 - 2026-05-19
- Job class name search field on the jobs index — filters the current status tab by class name substring; case-insensitive; persists across status tab switches
- Dynamic search: auto-submits after 4 characters typed or on clear (300 ms debounce) via a Stimulus
searchcontroller (app/javascript/solid_queue_web/search_controller.js) - Turbo Frame navigation on the jobs index — status filter tabs, search, and pagination update only the table region without a full page reload; URL is pushed to browser history so filtering is bookmarkable
importmap-railsintegration: engine registers its ownconfig/importmap.rb, addsapp/javascript/to the asset pipeline, and loads viajavascript_importmap_tagsin the layout@hotwired/turboimported in the engine entry point so Turbo is active within the engine's own layout- Recurring Tasks page (
/jobs/recurring_tasks) showing key, cron schedule, job class or command, queue, next run time, last run time, and Static/Dynamic badge; eager loads recurring executions to avoid N+1 - Recurring Tasks stat card on the dashboard (cyan, links to the page)
- "View recurring tasks" button in the dashboard Quick Links
sqd-badge--static(green) andsqd-badge--dynamic(purple) badge variants- Hamburger toggle nav for viewports narrower than 576px — three-bar button opens a full-width dropdown with vertically stacked links; no JS file required
sqd-grid-2utility class for responsive two-column layouts (collapses to one column at ≤768px).sqd-sr-onlyutility class for visually-hidden text:focus-visiblefocus ring (2px primary blue) for keyboard navigationaria-expandedon the mobile nav toggle, kept in sync on open/closerole="status"on notice flash messages androle="alert"on alert flash messagesaria-label="Main"on the primary navigation landmarkaria-current="page"on the active navigation linkscope="col"on all table header cells- Visually-hidden "Actions" label on empty action column headers
- Dashboard stat card order aligned with nav: Ready, Scheduled, Running, Blocked, Failed, Queues, Recurring, Processes
- Stat grid minimum cell width reduced from 150px to 128px so all 8 cards fit in one row
- Navbar title and links constrained to the same max-width as page content so they align horizontally with the dashboard
- Page headers stack vertically on mobile (≤640px)
- Stat grid uses a smaller minimum cell width on mobile
- Cards scroll horizontally on mobile to accommodate wide tables
- Main content padding reduced on mobile
0.4.0 - 2026-05-18
- Turbo Streams on the jobs list — discarding a single job removes its row in place; the last job swaps the card to an empty state without a full page reload
- Turbo Frame on the jobs list — status filter tabs, queue filter links, Discard All button, and pagination all update in place without reloading the page header or flash
- Dashboard stat cards are now clickable links to their respective filtered views
- GitHub Releases created automatically with CHANGELOG notes when a version tag is pushed
turbo-rails >= 2.0added as a runtime dependency
JobsControllerrefactored: execution model mapping moved toEXECUTION_MODELShash constant, eliminating twocasestatementsJobsController#destroyand#discard_allshare abefore_action :set_status_and_queueand afiltered_scopehelper, removing duplicated param reading and scope building
- Test suite reaches 100% line coverage; rescue paths,
derive_statusbranches (scheduled, finished), and the authentication block are all exercised
0.3.0 - 2026-05-18
- Processes page showing workers, dispatchers, and supervisors with kind, PID, host, metadata, last heartbeat, and Healthy/Stale status
- Queue pause / resume — Pause and Resume buttons per row on the Queues page
- Pagination for jobs and failed jobs lists via pagy (25 per page)
- Jobs URL segment renamed from
/jobs/jobsto/jobs/list - Job detail page showing status, queue, priority, arguments (pretty-printed JSON), and full error backtrace for failed jobs
- Retry/Discard action buttons on the detail page based on job status
- Job class names on the jobs and failed jobs index pages link to the detail page
- Retry and discard actions on individual failed jobs
- Bulk "Retry All" and "Discard All" actions for failed jobs
- Discard action on individual ready, scheduled, and blocked jobs
- Bulk "Discard All" action for ready, scheduled, and blocked jobs (scoped to current queue filter)
- Roadmap section added to README with planned features and contribution guidelines
- Failed jobs view now renders error class and message correctly (seed data format and missing CSS class)
0.2.0 - 2026-05-18
- CI release job publishes gem to RubyGems automatically on version tags
- Trusted Publishing via OIDC — no stored API keys required
- CSS is now inlined in the layout via a helper — no asset pipeline (Sprockets/Propshaft) required in the host app
- Renamed gem from
solid_queue_dashboardtosolid_queue_webto avoid conflict with an existing RubyGems package
0.1.0 - 2026-05-18
- Rails engine scaffold with isolated namespace
SolidQueueWeb - Configurable authentication hook (
SolidQueueWeb.authenticate) - Read-only dashboard with stat cards for ready, scheduled, running, blocked, and failed jobs
- Queues index page showing all queues sorted by name
- Jobs index with status filter tabs (ready, scheduled, claimed, blocked, failed) and per-queue filtering
- Failed jobs index page
- Minimal inline CSS with stat cards, tables, and status badges — no external CSS framework required
- Runtime dependencies:
rails >= 8.1.3,solid_queue >= 1.0 - RSpec test suite with dummy Rails app for engine request specs
- SimpleCov code coverage reporting
- RuboCop linting via
rubocop-rails-omakase;bundle exec rakeruns the full suite - CI workflow with lint (RuboCop) and test (RSpec) matrix across Ruby 3.3, 3.4, and 4.0
bin/releasescript for versioned gem releases