diff --git a/package_manager/src/ComposerInspector.php b/package_manager/src/ComposerInspector.php
index 588b11d64e11ed1edbb9a3b92b362b4b22ae22af..ed905603772a107574f51ff91943c37b1e4b4e92 100644
--- a/package_manager/src/ComposerInspector.php
+++ b/package_manager/src/ComposerInspector.php
@@ -50,15 +50,11 @@ class ComposerInspector implements LoggerAwareInterface {
   /**
    * A semantic version constraint for the supported version(s) of Composer.
    *
-   * Only versions supported by Composer are supported: the LTS and the latest
-   * minor version. Those are currently (May 2023) 2.2 and 2.5, but will be 2.5
-   * and 2.6 later this year (December 2023): the 2.2 LTS ends in December 2023.
-   *
    * @see https://endoflife.date/composer
    *
    * @var string
    */
-  final public const SUPPORTED_VERSION = '~2.5.5 || ^2.6';
+  final public const SUPPORTED_VERSION = '^2.6';
 
   /**
    * Constructs a ComposerInspector object.
diff --git a/package_manager/tests/fixtures/fake_site/composer.json b/package_manager/tests/fixtures/fake_site/composer.json
index 0158b2eded763a12554ef955a46d577e7754593e..a7f2b4cd69dff7efcf68ea89ce63ac81412e19a6 100644
--- a/package_manager/tests/fixtures/fake_site/composer.json
+++ b/package_manager/tests/fixtures/fake_site/composer.json
@@ -23,39 +23,11 @@
     "baz": null
   },
   "repositories": {
-    "packagist.org": false,
-    "drupal/core-recommended": {
-      "type": "path",
-      "version": "9.8.0",
-      "url": "../path_repos/drupal--core-recommended",
-      "options": {
-        "symlink": false
-      }
+    "fake-packages": {
+      "type": "composer",
+      "url": "./"
     },
-    "drupal/core-dev": {
-      "type": "path",
-      "version": "9.8.0",
-      "url": "../path_repos/drupal--core-dev",
-      "options": {
-        "symlink": false
-      }
-    },
-    "drupal/core": {
-      "type": "path",
-      "version": "9.8.0",
-      "url": "../path_repos/drupal--core",
-      "options": {
-        "symlink": false
-      }
-    },
-    "cweagans/composer-patches": {
-      "type": "path",
-      "version": "1.7.333",
-      "url": "../path_repos/cweagans--composer-patches",
-      "options": {
-        "symlink": false
-      }
-    }
+    "packagist.org": false
   },
   "minimum-stability": "stable",
   "config": {
diff --git a/package_manager/tests/fixtures/fake_site/composer.lock b/package_manager/tests/fixtures/fake_site/composer.lock
index 5fbd98bb419dd3507585f3c0463c6a71caabd472..c0a62b3d7336121fb1c5a21f81a070c0db23676b 100644
--- a/package_manager/tests/fixtures/fake_site/composer.lock
+++ b/package_manager/tests/fixtures/fake_site/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "672a2db4be3ba5e4f025ddea6b7dacd7",
+    "content-hash": "0235f8f2d3ef91d87864b76e12838e89",
     "packages": [
         {
             "name": "drupal/core",
@@ -50,11 +50,7 @@
                     }
                 }
             },
-            "description": "A fake version of drupal/core",
-            "transport-options": {
-                "symlink": false,
-                "relative": true
-            }
+            "description": "A fake version of drupal/core"
         },
         {
             "name": "drupal/core-recommended",
@@ -65,11 +61,7 @@
                 "reference": "112e4f7cfe8312457cd0eb58dcbffebc148850d8"
             },
             "type": "project",
-            "description": "A fake version of drupal/core-recommended",
-            "transport-options": {
-                "symlink": false,
-                "relative": true
-            }
+            "description": "A fake version of drupal/core-recommended"
         }
     ],
     "packages-dev": [
@@ -82,11 +74,7 @@
                 "reference": "b99a99a11ff2779b5e4c5787dc43575382a3548c"
             },
             "type": "package",
-            "description": "A fake version of drupal/core-dev",
-            "transport-options": {
-                "symlink": false,
-                "relative": true
-            }
+            "description": "A fake version of drupal/core-dev"
         }
     ],
     "aliases": [],
@@ -96,5 +84,5 @@
     "prefer-lowest": false,
     "platform": [],
     "platform-dev": [],
-    "plugin-api-version": "2.3.0"
+    "plugin-api-version": "2.6.0"
 }
diff --git a/package_manager/tests/fixtures/fake_site/packages.json b/package_manager/tests/fixtures/fake_site/packages.json
new file mode 100644
index 0000000000000000000000000000000000000000..12a17cccff4b8f41cc1685143aa45a7a6a29bf04
--- /dev/null
+++ b/package_manager/tests/fixtures/fake_site/packages.json
@@ -0,0 +1,101 @@
+{
+    "packages": {
+        "drupal/core-recommended": {
+            "9.8.0": {
+                "name": "drupal/core-recommended",
+                "description": "A fake version of drupal/core-recommended",
+                "type": "project",
+                "version": "9.8.0",
+                "dist": {
+                    "type": "path",
+                    "url": "../path_repos/drupal--core-recommended",
+                    "reference": "112e4f7cfe8312457cd0eb58dcbffebc148850d8"
+                }
+            }
+        },
+        "drupal/core-dev": {
+            "9.8.0": {
+                "name": "drupal/core-dev",
+                "description": "A fake version of drupal/core-dev",
+                "type": "package",
+                "version": "9.8.0",
+                "dist": {
+                    "type": "path",
+                    "url": "../path_repos/drupal--core-dev",
+                    "reference": "b99a99a11ff2779b5e4c5787dc43575382a3548c"
+                }
+            }
+        },
+        "drupal/core": {
+            "9.8.0": {
+                "name": "drupal/core",
+                "type": "drupal-core",
+                "description": "A fake version of drupal/core",
+                "version": "9.8.0",
+                "extra": {
+                    "_readme": [
+                        "The 'drupal-scaffold' section below is needed because 'Drupal\\automatic_updates\\Validator\\ScaffoldFilePermissionsValidator'",
+                        "uses this section to determine which files to check. The actual composer.json file for drupal/core will have more files listed",
+                        "but this limited list is used in '\\Drupal\\Tests\\automatic_updates\\Kernel\\StatusCheck\\ScaffoldFilePermissionsValidatorTest'",
+                        "to ensure this section determines the file list."
+                    ],
+                    "drupal-scaffold": {
+                        "file-mapping": {
+                            "[project-root]/.editorconfig": "assets/scaffold/files/editorconfig",
+                            "[project-root]/.gitattributes": "assets/scaffold/files/gitattributes",
+                            "[web-root]/.csslintrc": "assets/scaffold/files/csslintrc",
+                            "[web-root]/.eslintignore": "assets/scaffold/files/eslintignore",
+                            "[web-root]/.eslintrc.json": "assets/scaffold/files/eslintrc.json",
+                            "[web-root]/.ht.router.php": "assets/scaffold/files/ht.router.php",
+                            "[web-root]/.htaccess": "assets/scaffold/files/htaccess",
+                            "[web-root]/example.gitignore": "assets/scaffold/files/example.gitignore",
+                            "[web-root]/index.php": "assets/scaffold/files/index.php",
+                            "[web-root]/INSTALL.txt": "assets/scaffold/files/drupal.INSTALL.txt",
+                            "[web-root]/README.md": "assets/scaffold/files/drupal.README.md",
+                            "[web-root]/robots.txt": "assets/scaffold/files/robots.txt",
+                            "[web-root]/update.php": "assets/scaffold/files/update.php",
+                            "[web-root]/web.config": "assets/scaffold/files/web.config",
+                            "[web-root]/sites/README.txt": "assets/scaffold/files/sites.README.txt",
+                            "[web-root]/sites/development.services.yml": "assets/scaffold/files/development.services.yml",
+                            "[web-root]/sites/example.settings.local.php": "assets/scaffold/files/example.settings.local.php",
+                            "[web-root]/sites/example.sites.php": "assets/scaffold/files/example.sites.php",
+                            "[web-root]/sites/default/default.services.yml": "assets/scaffold/files/default.services.yml",
+                            "[web-root]/sites/default/default.settings.php": "assets/scaffold/files/default.settings.php",
+                            "[web-root]/modules/README.txt": "assets/scaffold/files/modules.README.txt",
+                            "[web-root]/profiles/README.txt": "assets/scaffold/files/profiles.README.txt",
+                            "[web-root]/themes/README.txt": "assets/scaffold/files/themes.README.txt"
+                        }
+                    }
+                },
+                "dist": {
+                    "type": "path",
+                    "url": "../path_repos/drupal--core",
+                    "reference": "31fd2270701526555acae45a3601c777e35508d5"
+                }
+            }
+        },
+        "cweagans/composer-patches": {
+            "1.7.333": {
+                "name": "cweagans/composer-patches",
+                "description": "A fake version of cweagans/composer-patches",
+                "type": "composer-plugin",
+                "version": "1.7.333",
+                "extra": {
+                    "class": "\\cweagans\\Fake\\ComposerPatches"
+                },
+                "dist": {
+                    "type": "path",
+                    "url": "../path_repos/cweagans--composer-patches"
+                },
+                "require": {
+                    "composer-plugin-api": "^1.0 || ^2.0"
+                },
+                "autoload": {
+                    "psr-4": {
+                        "cweagans\\Fake\\": "src"
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json b/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json
index f4b54bb81ac534a787e2a608478d9ed2871aba3e..8831ea21904f3fd49fc017935040311e31d01848 100644
--- a/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json
+++ b/package_manager/tests/fixtures/fake_site/vendor/composer/installed.json
@@ -47,10 +47,6 @@
             },
             "installation-source": "dist",
             "description": "A fake version of drupal/core",
-            "transport-options": {
-                "symlink": false,
-                "relative": true
-            },
             "install-path": "../drupal/core"
         },
         {
@@ -65,10 +61,6 @@
             "type": "package",
             "installation-source": "dist",
             "description": "A fake version of drupal/core-dev",
-            "transport-options": {
-                "symlink": false,
-                "relative": true
-            },
             "install-path": "../drupal/core-dev"
         },
         {
@@ -83,10 +75,6 @@
             "type": "project",
             "installation-source": "dist",
             "description": "A fake version of drupal/core-recommended",
-            "transport-options": {
-                "symlink": false,
-                "relative": true
-            },
             "install-path": "../drupal/core-recommended"
         }
     ],
diff --git a/package_manager/tests/modules/fixture_manipulator/fixture_manipulator.services.yml b/package_manager/tests/modules/fixture_manipulator/fixture_manipulator.services.yml
index f83263a21384a8d6a18d2211d0f53980aa997714..4ea503f7188ab4c0f6d998b00cb1808840654780 100644
--- a/package_manager/tests/modules/fixture_manipulator/fixture_manipulator.services.yml
+++ b/package_manager/tests/modules/fixture_manipulator/fixture_manipulator.services.yml
@@ -3,3 +3,5 @@ services:
     autowire: true
   Drupal\fixture_manipulator\StageFixtureManipulator:
     decorates: 'PhpTuf\ComposerStager\API\Core\BeginnerInterface'
+  Drupal\fixture_manipulator\ProcessFactory:
+    decorates: 'PhpTuf\ComposerStager\API\Process\Factory\ProcessFactoryInterface'
diff --git a/package_manager/tests/modules/fixture_manipulator/src/FixtureManipulator.php b/package_manager/tests/modules/fixture_manipulator/src/FixtureManipulator.php
index 80a499c5a22555742a0a83cb93d2343bbf7c962f..af72eca2b2838421f8d4925622318e55eb0d6de7 100644
--- a/package_manager/tests/modules/fixture_manipulator/src/FixtureManipulator.php
+++ b/package_manager/tests/modules/fixture_manipulator/src/FixtureManipulator.php
@@ -377,18 +377,6 @@ class FixtureManipulator {
     }
   }
 
-  /**
-   * Ensures a path is writable.
-   *
-   * @param string $path
-   *   The path.
-   */
-  private static function ensureFilePathIsWritable(string $path): void {
-    if (!is_writable($path)) {
-      throw new \LogicException("'$path' is not writable.");
-    }
-  }
-
   /**
    * Creates an empty .git folder after being provided a path.
    */
@@ -587,31 +575,18 @@ class FixtureManipulator {
       $this->createPathRepo($package, $repo_path, NULL);
     }
 
-    // Register the repository, keyed by package name and version. This ensures
-    // each package is registered only once and its version can be updated (but
-    // must have unique versions).
-    $repo_key = "repo.$name";
-    if ($is_new_or_fork === 'fork') {
-      $repositories = json_decode(file_get_contents($this->dir . '/composer.json'), TRUE, flags: JSON_THROW_ON_ERROR)['repositories'];
-      // @todo consistently use 'version' or 'options.versions.PACKAGE_NAME', by fixing ComposerFixtureCreator in https://drupal.org/i/3347055
-      $original_version = isset($repositories[$name]['version']) ? $repositories[$name]['version'] : $repositories[$name]['options']['versions'][$name];
-      if ($package['version'] !== $original_version) {
-        $repo_key .= "--" . $package['version'];
-      }
-    }
-    $repository = json_encode([
+    // Add the package to the Composer repository defined for the site.
+    $packages_json = $this->dir . '/packages.json';
+    $packages_data = file_get_contents($packages_json);
+    $packages_data = json_decode($packages_data, TRUE, flags: JSON_THROW_ON_ERROR);
+
+    $version = $package['version'];
+    $package['dist'] = [
       'type' => 'path',
       'url' => $repo_path,
-      'options' => [
-        'symlink' => FALSE,
-        'versions' => [
-          $name => $package['version'],
-        ],
-        // @see https://getcomposer.org/repoprio
-        'canonical' => FALSE,
-      ],
-    ], JSON_UNESCAPED_SLASHES);
-    $this->runComposerCommand(['config', $repo_key, $repository]);
+    ];
+    $packages_data['packages'][$name][$version] = $package;
+    file_put_contents($packages_json, json_encode($packages_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
 
     return $repo_path;
   }
@@ -631,8 +606,8 @@ class FixtureManipulator {
     }
     // Update all the repos in the composer.json file to point to the new
     // repos at the absolute path.
-    $composer_json = file_get_contents($this->dir . '/composer.json');
-    file_put_contents($this->dir . '/composer.json', str_replace('../path_repos/', "$path_repo_base/", $composer_json));
+    $composer_json = file_get_contents($this->dir . '/packages.json');
+    file_put_contents($this->dir . '/packages.json', str_replace('../path_repos/', "$path_repo_base/", $composer_json));
     $this->runComposerCommand(['update', '--lock']);
     $this->runComposerCommand(['install']);
   }
diff --git a/package_manager/tests/modules/fixture_manipulator/src/ProcessFactory.php b/package_manager/tests/modules/fixture_manipulator/src/ProcessFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..3066e3b724a9648b8fad4fca2ddfbf00f0fe43ae
--- /dev/null
+++ b/package_manager/tests/modules/fixture_manipulator/src/ProcessFactory.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\fixture_manipulator;
+
+use PhpTuf\ComposerStager\API\Process\Factory\ProcessFactoryInterface;
+use PhpTuf\ComposerStager\API\Process\Service\ProcessInterface;
+
+/**
+ * Process factory that always sets the COMPOSER_MIRROR_PATH_REPOS env variable.
+ *
+ * This is necessary because the fake_site fixture is built from a Composer-type
+ * repository, which will normally try to symlink packages which are installed
+ * from local directories, which in turn will break Package Manager because it
+ * does not support symlinks pointing outside the main code base. The
+ * COMPOSER_MIRROR_PATH_REPOS environment variable forces Composer to mirror,
+ * rather than symlink, local directories when installing packages.
+ *
+ * @see \Drupal\fixture_manipulator\FixtureManipulator::setUpRepos()
+ */
+final class ProcessFactory implements ProcessFactoryInterface {
+
+  /**
+   * Constructs a ProcessFactory object.
+   *
+   * @param \PhpTuf\ComposerStager\API\Process\Factory\ProcessFactoryInterface $decorated
+   *   The decorated process factory service.
+   */
+  public function __construct(private readonly ProcessFactoryInterface $decorated) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function create(array $command): ProcessInterface {
+    $process = $this->decorated->create($command);
+
+    $env = $process->getEnv();
+    $env['COMPOSER_MIRROR_PATH_REPOS'] = '1';
+    return $process->setEnv($env);
+  }
+
+}
diff --git a/package_manager/tests/src/Kernel/ComposerInspectorTest.php b/package_manager/tests/src/Kernel/ComposerInspectorTest.php
index f4932ee6c52dc2b960b98c5ed216376f9e57d680..c38e5fc00cdff4da1c103df48d1bcaeed05a62ba 100644
--- a/package_manager/tests/src/Kernel/ComposerInspectorTest.php
+++ b/package_manager/tests/src/Kernel/ComposerInspectorTest.php
@@ -223,8 +223,8 @@ class ComposerInspectorTest extends PackageManagerKernelTestBase {
    * @testWith ["2.2.12", "<default>"]
    *   ["2.2.13", "<default>"]
    *   ["2.5.0", "<default>"]
-   *   ["2.5.5", null]
-   *   ["2.5.11", null]
+   *   ["2.5.5", "<default>"]
+   *   ["2.5.11", "<default>"]
    *   ["2.6.0", null]
    *   ["2.2.11", "<default>"]
    *   ["2.2.0-dev", "<default>"]
diff --git a/package_manager/tests/src/Kernel/FixtureManipulatorTest.php b/package_manager/tests/src/Kernel/FixtureManipulatorTest.php
index 7c6c9da198cc58b4fd3735e8edbf608f0727e04c..00b5c370dfec9f23c705f6c5edc3293d1513bc6f 100644
--- a/package_manager/tests/src/Kernel/FixtureManipulatorTest.php
+++ b/package_manager/tests/src/Kernel/FixtureManipulatorTest.php
@@ -145,10 +145,10 @@ class FixtureManipulatorTest extends PackageManagerKernelTestBase {
   public function testModifyPackageConfig(): void {
     // Assert ::modifyPackage() works with a package in an existing fixture not
     // created by ::addPackage().
-    $decode_composer_json = function ($package_name): array {
-      return json_decode(file_get_contents($this->dir . "/vendor/$package_name/composer.json"), TRUE, flags: JSON_THROW_ON_ERROR);
+    $decode_packages_json = function (): array {
+      return json_decode(file_get_contents($this->dir . "/packages.json"), TRUE, flags: JSON_THROW_ON_ERROR);
     };
-    $original_composer_json = $decode_composer_json('my/dev-package');
+    $original_packages_json = $decode_packages_json();
     (new ActiveFixtureManipulator())
       // @see ::setUp()
       ->modifyPackageConfig('my/dev-package', '2.1.0', ['description' => 'something else'], TRUE)
@@ -156,10 +156,10 @@ class FixtureManipulatorTest extends PackageManagerKernelTestBase {
     // Verify that the package is indeed properly installed.
     $this->assertSame('2.1.0', $this->inspector->getInstalledPackagesList($this->dir)['my/dev-package']?->version);
     // Verify that the original exists, but has no description.
-    $this->assertSame('my/dev-package', $original_composer_json['name']);
-    $this->assertArrayNotHasKey('description', $original_composer_json);
+    $this->assertSame('my/dev-package', $original_packages_json['packages']['my/dev-package']['2.1.0']['name']);
+    $this->assertArrayNotHasKey('description', $original_packages_json);
     // Verify that the description was updated.
-    $this->assertSame('something else', $decode_composer_json('my/dev-package')['description']);
+    $this->assertSame('something else', $decode_packages_json()['packages']['my/dev-package']['2.1.0']['description']);
 
     (new ActiveFixtureManipulator())
       // Add a key to an existing package.
@@ -167,9 +167,8 @@ class FixtureManipulatorTest extends PackageManagerKernelTestBase {
       // Change a key in an existing package.
       ->setVersion('my/dev-package', '3.2.1', TRUE)
       ->commitChanges();
-    $this->assertSame(['foo' => 'bar'], $decode_composer_json('my/package')['extra']);
+    $this->assertSame(['foo' => 'bar'], $decode_packages_json()['packages']['my/package']['1.2.3']['extra']);
     $this->assertSame('3.2.1', $this->inspector->getInstalledPackagesList($this->dir)['my/dev-package']?->version);
-
   }
 
   /**
diff --git a/scripts/PackageManagerFixtureCreator.php b/scripts/PackageManagerFixtureCreator.php
index db17dff6a428e626bd526a372f32511d9fe4c946..d3721b7b227b7902f547f715119d6fd889815b2d 100755
--- a/scripts/PackageManagerFixtureCreator.php
+++ b/scripts/PackageManagerFixtureCreator.php
@@ -41,13 +41,21 @@ final class PackageManagerFixtureCreator {
     $fixture_core_composer_data = $fixture_core_composer_file->read();
     $fixture_core_composer_data['extra']['drupal-scaffold']['file-mapping'] = $core_composer_data['extra']['drupal-scaffold']['file-mapping'];
     $fixture_core_composer_file->write($fixture_core_composer_data);
+
+    $fixture_packages_json = new JsonFile(static::FIXTURE_PATH . '/packages.json');
+    $fixture_packages_data = $fixture_packages_json->read();
+    foreach ($fixture_packages_data['packages']['drupal/core'] as &$release) {
+      $release['extra']['drupal-scaffold']['file-mapping'] = $core_composer_data['extra']['drupal-scaffold']['file-mapping'];
+    }
+    $fixture_packages_json->write($fixture_packages_data);
+
     $fs = new Filesystem();
     $fs->remove(static::FIXTURE_PATH . "/composer.lock");
     // Remove all the vendor folders but leave our 2 test files.
     // @see \Drupal\Tests\package_manager\Kernel\PathExcluder\VendorHardeningExcluderTest
-    self::removeAllExcept(self::FIXTURE_PATH . "/vendor", ['.htaccess', 'web.config']);
+    self::removeAllExcept(static::FIXTURE_PATH . "/vendor", ['.htaccess', 'web.config']);
 
-    static::doComposerInstall();
+    self::runComposerCommand(['install']);
     static::removeAllExcept(static::FIXTURE_PATH . '/vendor/composer', ['installed.json', 'installed.php']);
     $fs->remove(static::FIXTURE_PATH . '/vendor/autoload.php');
     print "\nFixture updated.\nRunning phpcbf";
@@ -70,7 +78,9 @@ final class PackageManagerFixtureCreator {
   private static function runComposerCommand(array $command): string {
     array_unshift($command, 'composer');
     $command[] = "--working-dir=" . static::FIXTURE_PATH;
-    $process = new Process($command);
+    $process = new Process($command, env: [
+      'COMPOSER_MIRROR_PATH_REPOS' => '1',
+    ]);
     $process->run();
     if (!$process->isSuccessful()) {
       throw new ProcessFailedException($process);
@@ -100,11 +110,4 @@ final class PackageManagerFixtureCreator {
     }
   }
 
-  /**
-   * Runs `composer install`.
-   */
-  private static function doComposerInstall(): void {
-    self::runComposerCommand(['install']);
-  }
-
 }