diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 27f360561893defa79491dafe6a74dc433c34f0c..3e5a5d3bf33e89562bec0c6112ec01e9bd6e5db0 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -215,8 +215,7 @@ workflow:
 # This job name is pre-defined by GitLab, it has to be "pages".
 pages:
   stage: deploy
-  needs:
-    - 'Check Code'
+  needs: [Check Code]
   image: python:3.12
   artifacts:
     paths:
diff --git a/docs/info/common.md b/docs/info/common.md
index b3660d58fedf339a33c74f3dcd1a5c4debdf3ac1..920831fe465dfc914f545036ef2eaa6a93b43eb1 100644
--- a/docs/info/common.md
+++ b/docs/info/common.md
@@ -169,8 +169,7 @@ A full example could look like the below code:
 ```
 drush-status:
   extends: .testing-job-base
-  needs:
-    - composer
+  needs: [composer]
   script:
     # Setup the webserver and calculate the database connection information (if needed).
     - !reference [ .setup-webserver ]
diff --git a/docs/info/variants.md b/docs/info/variants.md
index 957a006eae91dcb322a83d95df5aa5539c1413cf..96f58cc6bcbe3ef07dd23066644924bc035db6f1 100644
--- a/docs/info/variants.md
+++ b/docs/info/variants.md
@@ -55,8 +55,7 @@ nightwatch (previous major):
     - *opt-in-previous-major-rule
     - *skip-nightwatch-rule
     - *nightwatch-tests-exist-rule
-  needs:
-    - "composer (previous major)"
+  needs: [composer (previous major)]
 
 phpunit (previous major):
   extends: phpunit
@@ -64,8 +63,7 @@ phpunit (previous major):
     - *opt-in-previous-major-rule
     - *skip-phpunit-rule
     - *phpunit-tests-exist-rule
-  needs:
-    - "composer (previous major)"
+  needs: [composer (previous major)]
 ```
 
 If you want to add a new variant to your project, you can extend the existing jobs, and just alter the necessary versions. Here is an example for testing with Drupal 9:
@@ -80,13 +78,11 @@ composer (D9):
 
 nightwatch (D9):
   extends: nightwatch
-  needs:
-    - "composer (D9)"
+  needs: [composer (D9)]
 
 phpunit (D9):
   extends: phpunit
-  needs:
-    - "composer (D9)"
+  needs: [composer (D9)]
 ```
 
 If your module does not have Nightwatch or PHPUnit tests, you can remove that particular block.
diff --git a/includes/include.drupalci.main-d7.yml b/includes/include.drupalci.main-d7.yml
index 54d53d89a700f7409abb4d7c2c4bb83212e57727..352834333908fe5e409efa814030f89cc3e995ff 100644
--- a/includes/include.drupalci.main-d7.yml
+++ b/includes/include.drupalci.main-d7.yml
@@ -352,6 +352,7 @@ stages:
     - composer --version
     - composer config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
     - composer config repositories.0 composer https://packages.drupal.org/7
+    - echo -e "\e[0Ksection_start:`date +%s`:composer_require[collapsed=true]\r\e[0KComposer require"
     # Get dependencies that will be used at a later stage.
     - composer require --dev drupal/coder:^8.2@stable phpunit/phpunit symfony/filesystem drush/drush:^8.3.3
     - |
@@ -360,6 +361,7 @@ stages:
         composer require --dev drupal/composer:1.x-dev
         mv -v vendor/drupal/composer vendor/drush/drush/commands/composer
       fi
+    - echo -e "\e[0Ksection_end:`date +%s`:composer_require\r\e[0K"
     # Third-party modules loaded via the projects composer.json will be in
     # vendor/drupal but they need to be moved into sites/all/modules.
     # The 'drupal' and 'coder' directories must not be moved.
@@ -402,7 +404,7 @@ composer (max PHP version):
 
 phpcs:
   stage: validate
-  needs: ['composer']
+  needs: [composer]
   rules:
     - *skip-phpcs-rule
     - if: $_PHPCS_ALLOW_FAILURE == "1" || ($_ALL_VALIDATE_ALLOW_FAILURE == "1" && $_PHPCS_ALLOW_FAILURE != "0")
@@ -473,9 +475,8 @@ phpcs:
   rules:
     - *skip-phpunit-rule
     - *phpunit-tests-exist-rule
-  needs: ['composer']
-  extends:
-    - .test-variables
+  needs: [composer]
+  extends: .test-variables
   variables:
     SYMFONY_DEPRECATIONS_HELPER: weak
   services:
@@ -517,8 +518,7 @@ phpunit (max PHP version):
     - *opt-in-max-php-rule
     - *skip-phpunit-rule
     - *phpunit-tests-exist-rule
-  needs:
-    - 'composer (max PHP version)'
+  needs: [composer (max PHP version)]
 
 # -----------------
 # TEST-ONLY CHANGES
@@ -533,8 +533,6 @@ test-only changes:
       when: manual
   interruptible: true
   allow_failure: true
-  # Remove parallel definition, in case it was defined in a customized phpunit.
-  parallel:
   script:
     - cd $CI_PROJECT_DIR && pwd
     - *show-environment-variables
diff --git a/includes/include.drupalci.main.yml b/includes/include.drupalci.main.yml
index 5f4ffd463b78e658c1044c253913e7de77511e92..0e6eec6f3c3a790cca07c2bc2b68fbe52130d748 100644
--- a/includes/include.drupalci.main.yml
+++ b/includes/include.drupalci.main.yml
@@ -139,9 +139,8 @@
 # Check the internal Composer end code. This is necessary in case allow_failure:true is set in the Composer job or one of its variants.
 .check-composer-end-code: &check-composer-end-code
   - |
-    echo "COMPOSER_END_CODE=$COMPOSER_END_CODE"
     if [ "$COMPOSER_END_CODE" != "0" ]; then
-      printf "$DIVIDER\nERROR: The pre-requisite Composer job did not complete successfully.\nCheck the log of that job for details.$DIVIDER\n"
+      printf "$DIVIDER\nCOMPOSER_END_CODE=$COMPOSER_END_CODE\nERROR: The pre-requisite Composer job did not complete successfully.\nCheck the log of that job for details.$DIVIDER\n"
       exit 1;
     fi;
 
@@ -477,9 +476,11 @@ stages:
       fi
     - php expand_composer_json.php || EXPAND_COMPOSER_EXIT_CODE=$?
     - if [[ "$EXPAND_COMPOSER_EXIT_CODE" != "" ]]; then echo "EXPAND_COMPOSER_EXIT_CODE=$EXPAND_COMPOSER_EXIT_CODE"; exit $EXPAND_COMPOSER_EXIT_CODE; fi
+    - echo -e "\e[0Ksection_start:`date +%s`:composer_install[collapsed=true]\r\e[0KComposer install and require drush"
     - composer install $COMPOSER_EXTRA
     - rm expand_composer_json.php
     - *require-drush
+    - echo -e "\e[0Ksection_end:`date +%s`:composer_install\r\e[0K"
     # Get the actual core version being used.
     - export INSTALLED_DRUPAL_VERSION=$(php -r "include '$CI_PROJECT_DIR/vendor/autoload.php'; echo \Drupal::VERSION;")
     - echo "INSTALLED_DRUPAL_VERSION=$INSTALLED_DRUPAL_VERSION" >> build.env
@@ -603,8 +604,7 @@ composer-lint:
     - if: $_COMPOSER_LINT_ALLOW_FAILURE == "0" || $_ALL_VALIDATE_ALLOW_FAILURE == "0"
       allow_failure: false
     - when: on_success
-  needs:
-    - composer
+  needs: [composer]
   script:
     - cd $CI_PROJECT_DIR && pwd
     - composer --version
@@ -645,8 +645,7 @@ phpcs:
     - if: $_PHPCS_ALLOW_FAILURE == "0" || $_ALL_VALIDATE_ALLOW_FAILURE == "0"
       allow_failure: false
     - when: on_success
-  needs:
-    - composer
+  needs: [composer]
   script:
     - cd $CI_PROJECT_DIR && pwd
     # Does the project have its own phpcs config file?
@@ -714,8 +713,7 @@ phpcs:
     - *opt-in-current-rule
     - *skip-phpstan-rule
     - *phpstan-allow-failure-rule
-  needs:
-    - composer
+  needs: [composer]
   script:
     # Run from within project directory so paths are correct.
     - cd $DRUPAL_PROJECT_FOLDER && pwd
@@ -778,8 +776,7 @@ phpstan (max PHP version):
     - *skip-phpstan-rule
     - *check-max-php-version-rule
     - *phpstan-allow-failure-rule
-  needs:
-    - 'composer (max PHP version)'
+  needs: [composer (max PHP version)]
 
 phpstan (next minor):
   extends: phpstan
@@ -787,8 +784,7 @@ phpstan (next minor):
     - *opt-in-next-minor-rule
     - *skip-phpstan-rule
     - *phpstan-allow-failure-rule
-  needs:
-    - 'composer (next minor)'
+  needs: [composer (next minor)]
 
 phpstan (next major):
   extends: phpstan
@@ -796,64 +792,11 @@ phpstan (next major):
     - *opt-in-next-major-rule
     - *skip-phpstan-rule
     - *phpstan-allow-failure-rule
-  needs:
-    - 'composer (next major)'
+  needs: [composer (next major)]
   script:
     - *amend-core-requirements-next-major
     - !reference [phpstan, script]
 
-upgrade status:
-  extends: .testing-job-base
-  allow_failure:
-    exit_codes:
-      - 3 # The upgrade_status:analyze command returns 3 if there are any issues. This is considered an 'amber' warning, not a red 'failure'.
-  rules:
-    - *skip-upgrade-status-rule
-    - exists:
-        - '*.info.yml'
-      when: on_success
-  needs:
-    - composer
-  script:
-    - cd $CI_PROJECT_DIR && pwd
-    - *check-composer-end-code
-    - *show-environment-variables
-    - *setup-webserver
-    - *simpletest-db
-    - *show-context
-    - composer require drush/drush drupal/upgrade_status $_UPGRADE_STATUS_COMPOSER_EXTRA
-    - php $_WEB_ROOT/core/scripts/drupal install standard
-    - vendor/bin/drush --root=$_WEB_ROOT st
-    - echo "PROJECT_NAME=$PROJECT_NAME, PROJECT_TYPE=$PROJECT_TYPE"
-    - |
-      if [[ "$PROJECT_TYPE" == "module" ]]; then
-        vendor/bin/drush --root=$_WEB_ROOT -y en upgrade_status $PROJECT_NAME
-      elif [[ "$PROJECT_TYPE" == "theme" ]]; then
-        vendor/bin/drush --root=$_WEB_ROOT -y en upgrade_status
-        vendor/bin/drush --root=$_WEB_ROOT -y theme:enable $PROJECT_NAME
-      else
-        echo "Unsupported type '$PROJECT_TYPE' defined in $PROJECT_NAME.info.yml. Only modules and themes can be tested with upgrade status."
-        exit 1
-      fi
-    # Output results via terminal and then generate the report.
-    - echo "Executing vendor/bin/drush --root=$_WEB_ROOT upgrade_status:analyze $PROJECT_NAME $_UPGRADE_STATUS_ANALYZE_EXTRA"
-    - vendor/bin/drush --root=$_WEB_ROOT upgrade_status:analyze $PROJECT_NAME $_UPGRADE_STATUS_ANALYZE_EXTRA || EXIT_CODE=$?
-    - vendor/bin/drush --root=$_WEB_ROOT upgrade_status:analyze $PROJECT_NAME $_UPGRADE_STATUS_ANALYZE_EXTRA --format=codeclimate > upgrade-status-analysis.json || true
-    # Reduce all escaped forward slashes \/ to a non-escaped forward slash /
-    - sed -i "s#\\\/#\/#g" upgrade-status-analysis.json || true
-    # Ensure paths are git-relative, by removing the path to the project to leave just the filenames.
-    - sed -i "s#$DRUPAL_PROJECTS_PATH/$CI_PROJECT_NAME/##g" upgrade-status-analysis.json || true
-    - echo "Exiting with EXIT_CODE=$EXIT_CODE"
-    - exit $EXIT_CODE
-  artifacts:
-    when: always
-    expose_as: 'upgrade-status-analysis'
-    reports:
-      codequality: upgrade-status-analysis.json
-    name: artifacts-$CI_PIPELINE_ID-$CI_JOB_NAME_SLUG
-    paths:
-      - upgrade-status-analysis.json
-
 stylelint:
   stage: validate
   allow_failure: true
@@ -867,8 +810,7 @@ stylelint:
       allow_failure: false
     - <<: *css-files-exist
       when: on_success
-  needs:
-    - composer
+  needs: [composer]
   script:
     - cd $CI_PROJECT_DIR/$_WEB_ROOT/core && corepack enable && yarn add @gitlab-formatters/stylelint-formatter-gitlab
     # Change directory to the project root folder.
@@ -903,8 +845,7 @@ eslint:
       allow_failure: false
     - <<: *js-and-yml-files-exist
       when: on_success
-  needs:
-    - composer
+  needs: [composer]
   script:
     # Change directory to the project root folder
     - cd $DRUPAL_PROJECT_FOLDER && pwd
@@ -973,8 +914,7 @@ cspell:
       allow_failure: false
     - when: on_success
   allow_failure: true
-  needs:
-    - composer
+  needs: [composer]
   artifacts:
     expose_as: junit
     expire_in: 6 mos
@@ -1036,8 +976,7 @@ cspell:
     - *with-database
     - *with-chrome
     - *with-chrome-legacy
-  extends:
-    - .test-variables
+  extends: .test-variables
 
 # ----------
 # NIGHTWATCH
@@ -1061,14 +1000,12 @@ cspell:
     echo -e "Nightwatch version $NIGHTWATCH_VERSION\n- Webdriver: $DRUPAL_TEST_WEBDRIVER_HOSTNAME:$DRUPAL_TEST_WEBDRIVER_PORT (w3c: $DRUPAL_TEST_WEBDRIVER_W3C)"
 
 .nightwatch-base:
-  extends:
-    - .testing-job-base
+  extends: .testing-job-base
   rules:
     - *opt-in-current-rule
     - *skip-nightwatch-rule
     - *nightwatch-tests-exist-rule
-  needs:
-    - composer
+  needs: [composer]
   variables:
     DRUPAL_TEST_BASE_URL: $SIMPLETEST_BASE_URL
     DRUPAL_TEST_WEBDRIVER_CHROME_ARGS: '--disable-dev-shm-usage --disable-gpu --headless'
@@ -1121,8 +1058,7 @@ cspell:
       - $_WEB_ROOT/core/reports/nightwatch
 
 nightwatch:
-  extends:
-    - .nightwatch-base
+  extends: .nightwatch-base
 
 nightwatch (max PHP version):
   extends: nightwatch
@@ -1131,8 +1067,7 @@ nightwatch (max PHP version):
     - *skip-nightwatch-rule
     - *check-max-php-version-rule
     - *nightwatch-tests-exist-rule
-  needs:
-    - 'composer (max PHP version)'
+  needs: [composer (max PHP version)]
   allow_failure: true
 
 nightwatch (previous minor):
@@ -1141,8 +1076,7 @@ nightwatch (previous minor):
     - *opt-in-previous-minor-rule
     - *skip-nightwatch-rule
     - *nightwatch-tests-exist-rule
-  needs:
-    - 'composer (previous minor)'
+  needs: [composer (previous minor)]
 
 nightwatch (previous major):
   extends: nightwatch
@@ -1150,8 +1084,7 @@ nightwatch (previous major):
     - *opt-in-previous-major-rule
     - *skip-nightwatch-rule
     - *nightwatch-tests-exist-rule
-  needs:
-    - 'composer (previous major)'
+  needs: [composer (previous major)]
 
 nightwatch (next minor):
   allow_failure: true
@@ -1160,8 +1093,7 @@ nightwatch (next minor):
     - *opt-in-next-minor-rule
     - *skip-nightwatch-rule
     - *nightwatch-tests-exist-rule
-  needs:
-    - 'composer (next minor)'
+  needs: [composer (next minor)]
 
 nightwatch (next major):
   allow_failure: true
@@ -1170,8 +1102,7 @@ nightwatch (next major):
     - *opt-in-next-major-rule
     - *skip-nightwatch-rule
     - *nightwatch-tests-exist-rule
-  needs:
-    - 'composer (next major)'
+  needs: [composer (next major)]
   script:
     - *amend-core-requirements-next-major
     - !reference [nightwatch, script]
@@ -1198,14 +1129,12 @@ nightwatch (next major):
 # A hidden re-usable job. Useful when using a job matrix.
 # A matrix example: https://git.drupalcode.org/project/keycdn
 .phpunit-base:
-  extends:
-    - .testing-job-base
+  extends: .testing-job-base
   rules:
     - *opt-in-current-rule
     - *skip-phpunit-rule
     - *phpunit-tests-exist-rule
-  needs:
-    - composer
+  needs: [composer]
   variables:
     SYMFONY_DEPRECATIONS_HELPER: 'disabled'
   script:
@@ -1275,8 +1204,7 @@ nightwatch (next major):
       - apache.error.log.txt
 
 phpunit:
-  extends:
-    - .phpunit-base
+  extends: .phpunit-base
 
 phpunit (max PHP version):
   extends: phpunit
@@ -1285,8 +1213,7 @@ phpunit (max PHP version):
     - *skip-phpunit-rule
     - *check-max-php-version-rule
     - *phpunit-tests-exist-rule
-  needs:
-    - 'composer (max PHP version)'
+  needs: [composer (max PHP version)]
   allow_failure: true
 
 phpunit (previous minor):
@@ -1295,8 +1222,7 @@ phpunit (previous minor):
     - *opt-in-previous-minor-rule
     - *skip-phpunit-rule
     - *phpunit-tests-exist-rule
-  needs:
-    - 'composer (previous minor)'
+  needs: [composer (previous minor)]
 
 phpunit (previous major):
   extends: phpunit
@@ -1304,8 +1230,7 @@ phpunit (previous major):
     - *opt-in-previous-major-rule
     - *skip-phpunit-rule
     - *phpunit-tests-exist-rule
-  needs:
-    - 'composer (previous major)'
+  needs: [composer (previous major)]
 
 # Future versions, hence breaks may arrive at any time: failure is allowed.
 phpunit (next minor):
@@ -1315,8 +1240,7 @@ phpunit (next minor):
     - *opt-in-next-minor-rule
     - *skip-phpunit-rule
     - *phpunit-tests-exist-rule
-  needs:
-    - 'composer (next minor)'
+  needs: [composer (next minor)]
 
 phpunit (next major):
   allow_failure: true
@@ -1325,8 +1249,7 @@ phpunit (next major):
     - *opt-in-next-major-rule
     - *skip-phpunit-rule
     - *phpunit-tests-exist-rule
-  needs:
-    - 'composer (next major)'
+  needs: [composer (next major)]
   script:
     - *amend-core-requirements-next-major
     - !reference [phpunit, script]
@@ -1334,6 +1257,7 @@ phpunit (next major):
 # -----------------
 # TEST-ONLY CHANGES
 # -----------------
+
 test-only changes:
   extends: phpunit
   rules:
@@ -1356,6 +1280,61 @@ test-only changes:
     # Execute using source so that all env vars will be available.
     - source ./test-only.sh
 
+# --------------
+# UPGRADE STATUS
+# --------------
+
+upgrade status:
+  extends: .testing-job-base
+  allow_failure:
+    exit_codes:
+      - 3 # The upgrade_status:analyze command returns 3 if there are any issues. This is considered an 'amber' warning, not a red 'failure'.
+  rules:
+    - *skip-upgrade-status-rule
+    - exists:
+        - '*.info.yml'
+      when: on_success
+  needs: [composer]
+  script:
+    - cd $CI_PROJECT_DIR && pwd
+    - *check-composer-end-code
+    - *show-environment-variables
+    - *setup-webserver
+    - *simpletest-db
+    - *show-context
+    - composer require drush/drush drupal/upgrade_status $_UPGRADE_STATUS_COMPOSER_EXTRA
+    - php $_WEB_ROOT/core/scripts/drupal install standard
+    - vendor/bin/drush --root=$_WEB_ROOT st
+    - echo "PROJECT_NAME=$PROJECT_NAME, PROJECT_TYPE=$PROJECT_TYPE"
+    - |
+      if [[ "$PROJECT_TYPE" == "module" ]]; then
+        vendor/bin/drush --root=$_WEB_ROOT -y en upgrade_status $PROJECT_NAME
+      elif [[ "$PROJECT_TYPE" == "theme" ]]; then
+        vendor/bin/drush --root=$_WEB_ROOT -y en upgrade_status
+        vendor/bin/drush --root=$_WEB_ROOT -y theme:enable $PROJECT_NAME
+      else
+        echo "Unsupported type '$PROJECT_TYPE' defined in $PROJECT_NAME.info.yml. Only modules and themes can be tested with upgrade status."
+        exit 1
+      fi
+    # Output results via terminal and then generate the report.
+    - echo "Executing vendor/bin/drush --root=$_WEB_ROOT upgrade_status:analyze $PROJECT_NAME $_UPGRADE_STATUS_ANALYZE_EXTRA"
+    - vendor/bin/drush --root=$_WEB_ROOT upgrade_status:analyze $PROJECT_NAME $_UPGRADE_STATUS_ANALYZE_EXTRA || EXIT_CODE=$?
+    - vendor/bin/drush --root=$_WEB_ROOT upgrade_status:analyze $PROJECT_NAME $_UPGRADE_STATUS_ANALYZE_EXTRA --format=codeclimate > upgrade-status-analysis.json || true
+    # Reduce all escaped forward slashes \/ to a non-escaped forward slash /
+    - sed -i "s#\\\/#\/#g" upgrade-status-analysis.json || true
+    # Ensure paths are git-relative, by removing the path to the project to leave just the filenames.
+    - sed -i "s#$DRUPAL_PROJECTS_PATH/$CI_PROJECT_NAME/##g" upgrade-status-analysis.json || true
+    - echo "Exiting with EXIT_CODE=$EXIT_CODE"
+    - exit $EXIT_CODE
+  artifacts:
+    when: always
+    expose_as: 'upgrade-status-analysis'
+    reports:
+      codequality: upgrade-status-analysis.json
+    name: artifacts-$CI_PIPELINE_ID-$CI_JOB_NAME_SLUG
+    paths:
+      - upgrade-status-analysis.json
+
 #################
 # .PRE stage Jobs
 #################