Ascender forked AWX 22.5.0 in August 2023 and has been diverging ever since, but there is no single page that says how. I have gone through the whole commit history of ctrliq/ascender to find out: here is what Ascender has that AWX does not.
Sections, and the entries inside them, are alphabetical rather than ordered by importance.
Authentication and credentials
Azure AD tenant OAuth2 (#238)
The tenant scoped backend with its own settings screen, separate from the plain Azure AD AWX ships.
CIQ Depot credential (#34)
A managed credential type carrying a Depot server, user and token into the job environment.
CIQ Mountain Access Token credential (#12)
A managed credential type that injects a MOUNTAIN_TOKEN into the job environment.
Configurable SameSite on the login cookie (#73)
The userLoggedIn cookie takes a USER_COOKIE_SAMESITE policy, which AWX does not set at all.
Custom SAML button label (#622)
The SAML login button's label is configurable instead of fixed.
Faster OIDC login (#662)
Organization and team memberships are reconciled in bulk instead of one query at a time.
Galaxy credentials need no organization (#224)
A Galaxy credential can exist outside any organization, which AWX requires it to belong to.
GitHub App credential (#239, thread)
An installation token lookup, so private repos use scoped, short lived tokens instead of PATs.
GitHub App private key from another credential (#240, thread)
The private key can be sourced from another credential rather than pasted into this one.
GitHub App SSH username for SCM URLs (#485, thread)
SCM URLs accept a GitHub App SSH username, so ssh remotes work with an App credential.
Labelled social auth buttons (#622)
Login buttons carry the provider name rather than a bare icon.
Social logins start with a POST form (#784)
The handoff to the provider is submitted as a POST form rather than followed as a plain link.
Inventory
allow_deletes_while_in_use (#785, thread)
A per inventory opt out of the 409 that blocks host deletion while jobs run against the inventory.
Federated inventory (#361, docs #362, thread)
A fourth inventory kind that fans a job into one slice per source, each on its own instance groups.
Instance groups on inventory sources (#600)
A sync resolves the source's own instance groups first, so it runs on a node that can reach it.
VMware source plugin choice (#773)
A dropdown picks the deprecated community.vmware plugin or its vmware.vmware replacement.
Jobs and operations
check_instance_ready management command (#576)
A readiness probe for Kubernetes deployments, so a pod reports ready only when the instance is.
DISABLE_ALL_SCHEDULES (#326)
A DR standby skips the periodic scheduler and reports every schedule disabled, without storing it.
Job wait and run time on the metrics endpoint (#981)
Five gauges say how long jobs waited and how long they ran, computed from what the database already stored.
Legacy Ansible 2.9 collections path (#145)
An opt in setting that restores ANSIBLE_COLLECTIONS_PATHS for old execution environments.
Pinned hosts on sliced jobs (#565)
Hosts marked as pinned run in every slice of a sliced job instead of landing in only one of them.
Project sync on repository push (#563, thread)
Projects gain a webhook service, key and ref filter, where AWX webhooks only launch templates.
Streamed job stdout downloads (#972)
The download view streams the file instead of buffering it whole into a web worker first.
Notifications
A timeout on outgoing notification requests (#670, thread)
Deliveries no longer hang indefinitely when the receiving endpoint stops answering.
Ledger as a logging aggregator (#73, thread)
Ledger joins logstash, splunk, loggly and sumologic, wiring in ascender-ledger.
Notify on change (#696, thread)
A fourth notification type beside started, success and error, fired when a run reports changes.
Revert buttons on notification secret fields (#331, thread)
A secret edited by mistake can be put back without retyping the stored value.
The job's host list in custom templates (#617, thread)
Custom bodies can use job.hosts, the names the job ran against, capped at 1000 entries.
Structural divergence
A vendored django-ansible-base subset (#741)
Part of django-ansible-base lives in awx/dab rather than being pulled as a dependency.
ARM64 production images (#366, #740)
Release images are built for ARM64 as well as x86_64, on a separate build job.
ascender-manage, and ascender as the import name (#982, #988)
The management command answers to both names, and every module imports as ascender as well as awx.
awx.awx renamed ctrliq.ascender (#432)
The bundled collection carries the Ascender namespace, and its modules have been updated with it.
awxkit renamed ascenderkit (#660)
The client has moved out to ascender-kit, and its awx command is now ascender.
axios replaced by native fetch (#287)
The API client has dropped its axios dependency for the browser's own fetch.
Django 4.2 to 6.1.1 (#187, #814)
Two framework majors, 4.2 to 5.2 and then 5.2 to 6.1, with djangorestframework moving up alongside.
drf-spectacular instead of coreapi (#226)
The API schema generator has changed, adding the schema, swagger and redoc endpoints.
Enzyme replaced by React Testing Library (#488)
Every suite has been converted first, and the enzyme stack is gone now that nothing uses it.
ESLint 8 to 9 (#391)
The flat eslint.config.mjs has replaced .eslintrc, in the same change that carries the i18n stack to Lingui 6.
formik replaced by a field layer of its own (#963)
formik is out of package.json: 681 lines of field layer stand where it did, and 183 files changed an import.
Insights and the Red Hat subscription surface removed (#931, #932, #933, #948)
The project type, the credential kind and the inventory settings are gone, and licensing.py no longer calls Red Hat.
JavaScript converted to TypeScript (#919)
The UI is TypeScript throughout, with no .js or .jsx left under awx/ui/src.
Jest replaced by Vitest (#914)
The UI suite has moved off Jest onto Vitest, every assertion unchanged, ahead of the bundler move that followed.
Lingui 3 to 6 (#95, #160, #391, #764)
Three i18n majors, 3 to 4 inside a devel merge, then 4 to 5 and 5 to 6, with its messages becoming descriptors.
Node 16 to 22 (#150, #355)
Two runtime majors, 16 to 20 and then 20 to 22, so the tree no longer builds on the Node AWX shipped.
PatternFly 4 to 6 (#506, #519, #520)
Two design system majors, v4 to v5 then v5 to v6, with the classic UI kept rather than replaced.
Python 3.9 to 3.14 (#110, #225, #811)
Three interpreter moves off the 3.9 the fork imported, 3.11 then 3.12 then 3.14, in the images and the build.
React 17 to 19 (#501, #521)
Two React majors under the same screens, 17 to 18 and then 18 to 19.
react-query as the UI's cache (#954)
Every list read and every OPTIONS request goes through one cache, where useRequest cached nothing.
react-router 5 to 8 (#496, #504, #522)
The v5 Switch trees have become v6 Routes, and v7 and v8 have followed.
react-scripts replaced by Vite (#159, #916)
Create React App is gone, ejected to webpack first and then moved onto Vite, with the build config in the tree.
styled-components removed (#380, #962)
Taken one major forward first, then out altogether: 291 styled definitions and 76 css props are 151 stylesheets.
The test suite runs on PostgreSQL (#813)
AWX tests on SQLite; the suite has moved to the database Ascender is deployed against, and to the defects SQLite hides.
ui_next deleted (#810)
AWX has moved to its new interface; Ascender has removed it, modernised the classic UI, and dropped the leftover switch.
Unmaintained Django add-ons dropped (#807, #808, #809)
django-crum, djangorestframework-yaml and django-extensions have given way to in-tree code or to Django's own.
uv instead of pip-tools (#899, #908, #911)
The requirements and docs lockfiles compile with uv, where AWX still runs pip-compile, and the pip-tools config is gone.
uvicorn instead of uwsgi and daphne (#975, #964)
One ASGI server serves the API and the websockets, where AWX runs uwsgi beside daphne.
Valkey instead of Redis (#200)
Redis 7 has given way to Valkey 9 for the dispatcher, the websockets and the cache.
UI
Copy job output to the clipboard (#520)
A copy button beside download on the output toolbar, carried in by the PatternFly 6 upgrade.
Custom browser title (#365, #677)
The browser tab title is configurable instead of fixed to the product name.
Custom header logo (#365, #677, thread)
Branding reaches the page header, where AWX only allows a login page logo.
Custom theme upload (#898, thread)
An administrator adds a theme as CSS from Settings, next to the custom logo, with no image rebuild.
Date filtering in the search toolbar (#396)
List views can be narrowed by date range from the toolbar rather than by hand written filters.
Execution environment pull policy (#220)
The pull policy is shown in the execution environment detail view instead of being hidden.
Four themes (#588, thread)
Default, Classic (the old AWX look), Light and Dark, chosen per user from the page header.
Hindi and Arabic translations (#591)
Two more locales alongside the ones the interface already ships.
Labels listing view (#120, docs #657, thread)
Labels get their own screen, with a pseudo-folder workflow for organising them.
Per user preferred language (#335, thread)
The interface language is stored on the user profile instead of following the browser setting.
Variables editors size to their content (#806, thread)
The YAML and JSON editors grow with what they hold, between four and fifty lines, replacing the expand modal.
Workflows
Automatic ascender_stats_* artifacts (#562, thread)
Changed and failed flags plus host lists, merged across slices, with nothing in the playbook.
Automatic node retries (#564, thread)
A max_retries per node, capped at 100, with failure paths held back while attempts remain.
Conditional connectors (#561, issue #505, thread)
Beside success, failure and always, a link can test an artifact key produced by the parent job.
context_template on approval nodes (#578, thread)
Per node context rendered into the approval detail and its notifications, so approvers have facts.
Elapsed time on the workflow job output toolbar (#393)
A running workflow shows how long it has been going, matching the job output view.
Multi approver quorum approvals (#579, thread)
A required_approvals count, one recorded vote per user, and an on_timeout resolution.
Relaunch from failed, canceled or first node (#406, thread)
Nodes that have already succeeded are carried forward and still traverse their success paths.