diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 51b24f41e592e08e83739a34d680e24d38d71a4f..30a530cf9eadd4b92db7349ea4d97f11a1bf14f3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -47,6 +47,7 @@ variables:
   COMPOSER_ROOT_VERSION: "${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}${CI_COMMIT_BRANCH}-dev"
   CONCURRENCY: 24
   GIT_DEPTH: "3"
+  PARENT_PIPELINE_ID: $CI_PIPELINE_ID
 
 
 #############
@@ -79,37 +80,16 @@ stages:
     name: $_CONFIG_DOCKERHUB_ROOT/php-$_TARGET_PHP-apache:production
   rules:
     - if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project"
+    - if: $CI_PIPELINE_SOURCE == "schedule" && $CI_PROJECT_ROOT_NAMESPACE == "project"
     - if: $CI_PIPELINE_SOURCE == "merge_request_event"
 
-.composer-cache: &composer-cache
-  key:
-    files:
-      - ./composer.json
-      - ./composer.lock
-  paths:
-    - ./vendor
-
-.yarn-cache: &yarn-cache
-  key:
-    files:
-      - ./core/package.json
-      - ./core/yarn.lock
-  paths:
-    - ./core/node_modules
-
-.with-composer-cache: &with-composer-cache
-  cache:
-    policy: pull
-    <<: *composer-cache
+.with-composer: &with-composer
   needs:
     - '📦️ Composer'
 
-.with-yarn-cache: &with-yarn-cache
+.with-yarn: &with-yarn
   needs:
     - '📦️ Yarn'
-  cache:
-    policy: pull
-    <<: *yarn-cache
 
 .junit-artifacts: &junit-artifacts
   artifacts:
@@ -228,7 +208,12 @@ stages:
   <<: *default-job-settings-lint
   stage: 🏗️ Build
   cache:
-    <<: *composer-cache
+    key:
+      files:
+        - ./composer.json
+        - ./composer.lock
+    paths:
+      - ./vendor
   artifacts:
     expire_in: 1 week
     expose_as: 'web-vendor'
@@ -242,7 +227,12 @@ stages:
   <<: *default-job-settings-lint
   stage: 🏗️ Build
   cache:
-    <<: *yarn-cache
+    key:
+      files:
+        - ./core/package.json
+        - ./core/yarn.lock
+    paths:
+      - ./core/node_modules
   artifacts:
     expire_in: 1 week
     expose_as: 'yarn-vendor'
@@ -256,7 +246,7 @@ stages:
 ################
 
 '🧹 PHP Static Analysis (phpstan)':
-  <<: [ *with-composer-cache, *junit-artifacts, *default-job-settings-lint ]
+  <<: [ *with-composer, *junit-artifacts, *default-job-settings-lint ]
   stage: 🪄 Lint
   script:
     - php vendor/bin/phpstan analyze --configuration=./core/phpstan.neon.dist --error-format=gitlab > phpstan-quality-report.json
@@ -265,7 +255,7 @@ stages:
       codequality: phpstan-quality-report.json
 
 '🧹 PHP Coding standards (PHPCS)':
-  <<: [ *with-composer-cache, *junit-artifacts, *default-job-settings-lint ]
+  <<: [ *with-composer, *junit-artifacts, *default-job-settings-lint ]
   stage: 🪄 Lint
   script:
     - composer phpcs -- --report-full --report-summary --report-\\Micheh\\PhpCodeSniffer\\Report\\Gitlab=phpcs-quality-report.json
@@ -274,7 +264,7 @@ stages:
       codequality: phpcs-quality-report.json
 
 '🧹 JavaScript linting (eslint)':
-  <<: [ *with-yarn-cache, *junit-artifacts, *default-job-settings-lint ]
+  <<: [ *with-yarn, *junit-artifacts, *default-job-settings-lint ]
   stage: 🪄 Lint
   script:
     - yarn --cwd=./core run -s lint:core-js-passing --format gitlab
@@ -283,7 +273,7 @@ stages:
       codequality: eslint-quality-report.json
 
 '🧹 CSS linting (stylelint)':
-  <<: [ *with-yarn-cache, *junit-artifacts, *default-job-settings-lint ]
+  <<: [ *with-yarn, *junit-artifacts, *default-job-settings-lint ]
   stage: 🪄 Lint
   script:
     - yarn run --cwd=./core lint:css --color --custom-formatter=node_modules/stylelint-formatter-gitlab
@@ -292,14 +282,14 @@ stages:
       codequality: stylelint-quality-report.json
 
 '🧹 Compilation check':
-  <<: [ *with-yarn-cache, *default-job-settings-lint ]
+  <<: [ *with-yarn, *default-job-settings-lint ]
   stage: 🪄 Lint
   script:
     - yarn run --cwd=./core build:css --check
     - cd core && yarn run -s check:ckeditor5
 
 '📔 Spell-checking':
-  <<: [ *with-yarn-cache, *default-job-settings-lint ]
+  <<: [ *with-yarn, *default-job-settings-lint ]
   stage: 🪄 Lint
   script:
     - export TARGET_BRANCH=${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}${CI_COMMIT_BRANCH}
diff --git a/.gitlab-ci/pipeline.yml b/.gitlab-ci/pipeline.yml
index 08952b0041d204f8fb7eff80e9499b8973468163..efe95f486eba6df9e26effdac4b6894e2fe8a123 100644
--- a/.gitlab-ci/pipeline.yml
+++ b/.gitlab-ci/pipeline.yml
@@ -1,19 +1,6 @@
 # cspell:ignore drupaltestbot drupaltestbotpw
 
 stages:
-  ################
-  # Build
-  #
-  # Assemble the test environment.
-  ################
-  - 🏗️ Build
-
-  ################
-  # Test
-  #
-  # The test phase actually executes the tests, as well as gathering results
-  # and artifacts.
-  ################
   - 🗜️ Test
 
 #############
@@ -21,6 +8,7 @@ stages:
 #############
 
 .default-job-settings: &default-job-settings
+  stage: 🗜️ Test
   interruptible: true
   allow_failure: false
   retry:
@@ -36,36 +24,6 @@ stages:
   rules:
     - if: $CI_PIPELINE_SOURCE == "parent_pipeline"
 
-.composer-cache: &composer-cache
-  key:
-    files:
-      - ./composer.json
-      - ./composer.lock
-  paths:
-    - ./vendor
-
-.yarn-cache: &yarn-cache
-  key:
-    files:
-      - ./core/package.json
-      - ./core/yarn.lock
-  paths:
-    - ./core/node_modules
-
-.with-composer-cache: &with-composer-cache
-  needs:
-    - '📦️ Composer'
-  cache:
-    policy: pull
-    <<: *composer-cache
-
-.with-yarn-cache: &with-yarn-cache
-  needs:
-    - '📦️ Yarn'
-  cache:
-    policy: pull
-    <<: *yarn-cache
-
 .junit-artifacts: &junit-artifacts
   artifacts:
     expose_as: junit
@@ -75,13 +33,17 @@ stages:
     reports:
       junit: junit.xml
 
+.with-composer: &with-composer
+  needs:
+    - pipeline: $PARENT_PIPELINE_ID
+      job: '📦️ Composer'
+
 .with-composer-and-yarn: &with-composer-and-yarn
   needs:
-    - '📦️ Composer'
-    - '📦️ Yarn'
-  dependencies:
-    - '📦️ Yarn'
-    - '📦️ Composer'
+    - pipeline: $PARENT_PIPELINE_ID
+      job: '📦️ Composer'
+    - pipeline: $PARENT_PIPELINE_ID
+      job: '📦️ Yarn'
 
 .test-variables: &test-variables
   FF_NETWORK_PER_BUILD: 1
@@ -151,45 +113,8 @@ stages:
 # Documentation: https://docs.gitlab.com/ee/ci/jobs/
 ################
 
-################
-# Build Jobs
-################
-
-'📦️ Composer':
-  <<: *default-job-settings
-  stage: 🏗️ Build
-  cache:
-    <<: *composer-cache
-  artifacts:
-    expire_in: 1 week
-    expose_as: 'web-vendor'
-    paths:
-      - vendor/
-  script:
-      - composer validate
-      - composer install
-
-'📦️ Yarn':
-  <<: *default-job-settings
-  stage: 🏗️ Build
-  cache:
-    <<: *yarn-cache
-  artifacts:
-    expire_in: 1 week
-    expose_as: 'yarn-vendor'
-    paths:
-      - core/node_modules/
-  script:
-    # Installs all core javascript dependencies and adds junit formatter.
-    - yarn --cwd ./core add stylelint-junit-formatter
-
-################
-# Test Jobs
-################
-
 '🌐️️ PHPUnit Functional':
-  <<: [ *with-composer-cache, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
-  stage: 🗜️ Test
+  <<: [ *with-composer, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
   parallel: 7
   variables:
     <<: *test-variables
@@ -199,8 +124,7 @@ stages:
     - <<: *with-database
 
 '🩹 Test-only changes':
-  <<: [ *with-composer-cache, *phpunit-artifacts, *setup-webserver, *default-job-settings ]
-  stage: 🗜️ Test
+  <<: [ *with-composer, *phpunit-artifacts, *setup-webserver, *default-job-settings ]
   when: manual
   interruptible: true
   allow_failure: true
@@ -246,8 +170,7 @@ stages:
       fi
 
 '⚙️️ PHPUnit Kernel':
-  <<: [*with-composer-cache, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
-  stage: 🗜️ Test
+  <<: [*with-composer, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
   parallel: 2
   variables:
     <<: *test-variables
@@ -257,8 +180,7 @@ stages:
     - <<: *with-database
 
 '🖱️️️ PHPUnit Functional Javascript':
-  <<: [ *with-composer-cache, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
-  stage: 🗜️ Test
+  <<: [ *with-composer, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
   parallel: 2
   variables:
     <<: *test-variables
@@ -269,8 +191,7 @@ stages:
     - <<: *with-chrome
 
 '👷️️️ PHPUnit Build':
-  <<: [ *with-composer-cache, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
-  stage: 🗜️ Test
+  <<: [ *with-composer, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
   variables:
     <<: *test-variables
     TESTSUITE: PHPUnit-Build
@@ -279,8 +200,7 @@ stages:
     - <<: *with-database
 
 '⚡️ PHPUnit Unit':
-  <<: [ *with-composer-cache, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
-  stage: 🗜️ Test
+  <<: [ *with-composer, *phpunit-artifacts, *setup-webserver, *run-tests, *default-job-settings ]
   services:
     # There are some unit tests that need a database.
     # @todo Remove after https://www.drupal.org/project/drupal/issues/3386217
@@ -292,7 +212,6 @@ stages:
 
 '🦉️️️ Nightwatch':
   <<: [ *with-composer-and-yarn, *setup-webserver, *default-job-settings ]
-  stage: 🗜️ Test
   variables:
     <<: *test-variables
   services: