Subdivide derived root types so that Artifact class methods can always handle derived artifact paths properly regardless of the --experimental_sibling_repository_layout value.

PiperOrigin-RevId: 353665168
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
index 50d53c4..9d0fd47 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java
@@ -650,11 +650,6 @@
    * that this is available on every Artifact type, including source artifacts. As a matter of fact,
    * one of its most common use cases is to construct a derived artifact's output path out of a
    * sibling source artifact's by replacing the basename in its output-dir-relative path.
-   *
-   * <p>This is just a wrapper over {@link Artifact#getPathForLocationExpansion} at the moment.
-   * However, since it will be kept in sync with the output directory layout, which is planned for
-   * an overhaul, it must be preferred over {@link Artifact#getPathForLocationExpansion} whenever
-   * possible.
    */
   public PathFragment getOutputDirRelativePath(boolean siblingRepositoryLayout) {
     return getRootRelativePath();
@@ -665,10 +660,12 @@
    * path always starts with a corresponding package name, if exists.
    */
   public PathFragment getRepositoryRelativePath() {
-    PathFragment fullPath = getPathForLocationExpansion();
-    return fullPath.startsWith(LabelConstants.EXTERNAL_PATH_PREFIX)
-        ? fullPath.subFragment(2)
-        : fullPath;
+    PathFragment relativePath = getRootRelativePath();
+    // External artifacts under legacy roots are still prefixed with "external/<repo name>".
+    if (root.isLegacy() && relativePath.startsWith(LabelConstants.EXTERNAL_PATH_PREFIX)) {
+      relativePath = relativePath.subFragment(2);
+    }
+    return relativePath;
   }
 
   /** Returns this.getExecPath().getPathString(). */
@@ -794,13 +791,30 @@
    * runfiles tree. For local targets, it returns the rootRelativePath.
    */
   public final PathFragment getRunfilesPath() {
-    PathFragment relativePath = getOutputDirRelativePath(false);
-    // We can't use root.isExternalSource() here since it needs to handle derived artifacts too.
-    if (relativePath.startsWith(LabelConstants.EXTERNAL_PATH_PREFIX)) {
-      // Turn external/repo/foo into ../repo/foo.
-      relativePath = relativePath.relativeTo(LabelConstants.EXTERNAL_PATH_PREFIX);
-      relativePath = LabelConstants.EXTERNAL_RUNFILES_PATH_PREFIX.getRelative(relativePath);
+    PathFragment relativePath = getRootRelativePath();
+    // Runfile paths for external artifacts should be prefixed with "../<repo name>".
+    if (root.isLegacy()) {
+      // Root-relative paths of external artifacts under legacy roots are already prefixed with
+      // "external/<repo name>". Just replace "external" with "..".
+      if (relativePath.startsWith(LabelConstants.EXTERNAL_PATH_PREFIX)) {
+        relativePath = relativePath.relativeTo(LabelConstants.EXTERNAL_PATH_PREFIX);
+        relativePath = LabelConstants.EXTERNAL_RUNFILES_PATH_PREFIX.getRelative(relativePath);
+      }
+    } else {
+      if (root.isExternal()) {
+        // Both external source artifacts and external derived artifacts have their repo name as
+        // their 2nd level directory name in their exec paths.
+        // i.e. external/<repo name>/... and bazel-out/<repo name>/...
+        // This is a pure coincidence, and the below line needs to be updated if any of the
+        // directory structures change.
+        String repoName = getExecPath().getSegment(1);
+        relativePath =
+            LabelConstants.EXTERNAL_RUNFILES_PATH_PREFIX
+                .getRelative(repoName)
+                .getRelative(relativePath);
+      }
     }
+    // We can't use root.isExternalSource() here since it needs to handle derived artifacts too.
     return relativePath;
   }
 
@@ -896,7 +910,7 @@
 
     @Override
     public PathFragment getRootRelativePath() {
-      return root.isExternalSourceRoot() ? getExecPath().subFragment(2) : getExecPath();
+      return root.isExternal() ? getExecPath().subFragment(2) : getExecPath();
     }
 
     @Override
@@ -1123,8 +1137,10 @@
         PathFragment customDerivedTreeRoot) {
       return ArtifactRoot.asDerivedRoot(
           getExecRoot(treeArtifactRoot),
-          // e.g. bazel-out/{customDerivedTreeRoot}/k8-fastbuild/bin
           false,
+          false,
+          false,
+          // e.g. bazel-out/{customDerivedTreeRoot}/k8-fastbuild/bin
           getExecPathWithinCustomDerivedRoot(
               derivedPathPrefix, customDerivedTreeRoot, treeArtifactRoot.getExecPath()));
     }
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ArtifactRoot.java b/src/main/java/com/google/devtools/build/lib/actions/ArtifactRoot.java
index fa2ee4c..1db4622 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ArtifactRoot.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ArtifactRoot.java
@@ -84,7 +84,12 @@
    * <p>Be careful with this method - all derived roots must be within the derived artifacts tree,
    * defined in ArtifactFactory (see {@link ArtifactFactory#isDerivedArtifact(PathFragment)}).
    */
-  public static ArtifactRoot asDerivedRoot(Path execRoot, boolean isMiddleman, String... prefixes) {
+  public static ArtifactRoot asDerivedRoot(
+      Path execRoot,
+      boolean isMiddleman,
+      boolean isExternal,
+      boolean siblingRepositoryLayout,
+      String... prefixes) {
     PathFragment execPath = PathFragment.EMPTY_FRAGMENT;
     for (String prefix : prefixes) {
       // Tests can have empty segments here, be gentle to them.
@@ -92,7 +97,7 @@
         execPath = execPath.getChild(prefix);
       }
     }
-    return asDerivedRoot(execRoot, isMiddleman, execPath);
+    return asDerivedRoot(execRoot, isMiddleman, isExternal, siblingRepositoryLayout, execPath);
   }
 
   /**
@@ -102,7 +107,11 @@
    * defined in ArtifactFactory (see {@link ArtifactFactory#isDerivedArtifact(PathFragment)}).
    */
   public static ArtifactRoot asDerivedRoot(
-      Path execRoot, boolean isMiddleman, PathFragment execPath) {
+      Path execRoot,
+      boolean isMiddleman,
+      boolean isExternal,
+      boolean siblingRepositoryLayout,
+      PathFragment execPath) {
     // Make sure that we are not creating a derived artifact under the execRoot.
     Preconditions.checkArgument(!execPath.isEmpty(), "empty execPath");
     Preconditions.checkArgument(
@@ -110,20 +119,34 @@
         "execPath: %s contains parent directory reference (..)",
         execPath);
     Path root = execRoot.getRelative(execPath);
-    return INTERNER.intern(
-        new ArtifactRoot(
-            Root.fromPath(root), execPath, isMiddleman ? RootType.Middleman : RootType.Output));
+    RootType type;
+    if (siblingRepositoryLayout) {
+      type =
+          isMiddleman
+              ? (isExternal ? RootType.ExternalMiddleman : RootType.Middleman)
+              : (isExternal ? RootType.ExternalOutput : RootType.Output);
+    } else {
+      Preconditions.checkArgument(
+          !isExternal,
+          "external derived roots unavailable without --experimental_sibling_repository_layout");
+      type = isMiddleman ? RootType.LegacyMiddleman : RootType.LegacyOutput;
+    }
+    return INTERNER.intern(new ArtifactRoot(Root.fromPath(root), execPath, type));
   }
 
   @AutoCodec.VisibleForSerialization
   @AutoCodec.Instantiator
   static ArtifactRoot createForSerialization(
       Root rootForSerialization, PathFragment execPath, RootType rootType) {
-    if (rootType != RootType.Output) {
+    if (!isOutputRootType(rootType)) {
       return INTERNER.intern(new ArtifactRoot(rootForSerialization, execPath, rootType));
     }
     return asDerivedRoot(
-        rootForSerialization.asPath(), false, execPath.getSegments().toArray(new String[0]));
+        rootForSerialization.asPath(),
+        false,
+        rootType == RootType.ExternalOutput,
+        rootType != RootType.LegacyOutput,
+        execPath.getSegments().toArray(new String[0]));
   }
 
   @AutoCodec.VisibleForSerialization
@@ -131,7 +154,20 @@
     Source,
     Output,
     Middleman,
-    ExternalSource
+    // Root types for external repository artifacts. Note that ExternalOutput and ExternalMiddleman
+    // are activated only if --experimental_sibling_repository_layout is true, which will become
+    // the default value soon. The ExternalSource type is already in effect by default.
+    ExternalSource,
+    ExternalOutput,
+    ExternalMiddleman,
+    // Legacy root types for derived artifacts. Even though they're called legacy, they are still
+    // the default, but soon to be deprecated when --experimental_sibling_repository_layout is set
+    // true by default. In terms of the actual root paths, there are no differences between these
+    // and Output and Middleman. Their sole purpose is to embed the
+    // --experimental_sibling_repository_layout flag value information in Artifacts without
+    // additional storage cost.
+    LegacyOutput,
+    LegacyMiddleman
   }
 
   private final Root root;
@@ -171,15 +207,29 @@
   }
 
   public boolean isSourceRoot() {
-    return rootType == RootType.Source || isExternalSourceRoot();
+    return rootType == RootType.Source || rootType == RootType.ExternalSource;
   }
 
-  public boolean isExternalSourceRoot() {
-    return rootType == RootType.ExternalSource;
+  private static boolean isOutputRootType(RootType rootType) {
+    return rootType == RootType.Output
+        || rootType == RootType.ExternalOutput
+        || rootType == RootType.LegacyOutput;
   }
 
   boolean isMiddlemanRoot() {
-    return rootType == RootType.Middleman;
+    return rootType == RootType.Middleman
+        || rootType == RootType.ExternalMiddleman
+        || rootType == RootType.LegacyMiddleman;
+  }
+
+  public boolean isExternal() {
+    return rootType == RootType.ExternalSource
+        || rootType == RootType.ExternalOutput
+        || rootType == RootType.ExternalMiddleman;
+  }
+
+  public boolean isLegacy() {
+    return rootType == RootType.LegacyOutput || rootType == RootType.LegacyMiddleman;
   }
 
   @Override
@@ -200,7 +250,7 @@
    */
   @SuppressWarnings("unused") // Used by @AutoCodec.
   Root getRootForSerialization() {
-    if (rootType != RootType.Output) {
+    if (!isOutputRootType(rootType)) {
       return root;
     }
     // Find fragment of root that does not include execPath and return just that root. It is likely
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BlazeDirectories.java b/src/main/java/com/google/devtools/build/lib/analysis/BlazeDirectories.java
index a745476..60ea72c 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BlazeDirectories.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BlazeDirectories.java
@@ -206,7 +206,7 @@
    */
   public ArtifactRoot getBuildDataDirectory(String workspaceName) {
     return ArtifactRoot.asDerivedRoot(
-        getExecRoot(workspaceName), false, getRelativeOutputPath(productName));
+        getExecRoot(workspaceName), false, false, false, getRelativeOutputPath(productName));
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/OutputDirectories.java b/src/main/java/com/google/devtools/build/lib/analysis/config/OutputDirectories.java
index 267aab3..1e33c67 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/OutputDirectories.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/OutputDirectories.java
@@ -111,7 +111,13 @@
       Path execRoot = directories.getExecRoot(mainRepositoryName.strippedName());
       // e.g., [[execroot/repo1]/bazel-out/config/bin]
       return ArtifactRoot.asDerivedRoot(
-          execRoot, middleman, directories.getRelativeOutputPath(), outputDirName, nameFragment);
+          execRoot,
+          middleman,
+          false,
+          false,
+          directories.getRelativeOutputPath(),
+          outputDirName,
+          nameFragment);
     }
   }
 
@@ -216,6 +222,8 @@
     return ArtifactRoot.asDerivedRoot(
         execRoot,
         isMiddleman,
+        !repository.isMain() && !repository.isDefault(),
+        true,
         directories.getRelativeOutputPath(),
         repository.strippedName(),
         outputDirName,
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/actions/NinjaGraphArtifactsHelper.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/actions/NinjaGraphArtifactsHelper.java
index 7ce8a66..e2b499d 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/actions/NinjaGraphArtifactsHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/ninja/actions/NinjaGraphArtifactsHelper.java
@@ -77,7 +77,7 @@
             .getExecRoot(ruleContext.getWorkspaceName());
     this.derivedOutputRoot =
         ArtifactRoot.asDerivedRoot(
-            execRoot, false, outputRootPath.getSegments().toArray(new String[0]));
+            execRoot, false, false, false, outputRootPath.getSegments().toArray(new String[0]));
     this.sourceRoot = ruleContext.getRule().getPackage().getSourceRoot().get();
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ActionCacheCheckerTest.java b/src/test/java/com/google/devtools/build/lib/actions/ActionCacheCheckerTest.java
index 33be232..7e72c7e 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ActionCacheCheckerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ActionCacheCheckerTest.java
@@ -365,7 +365,11 @@
     SpecialArtifact output =
         new Artifact.SpecialArtifact(
             ArtifactRoot.asDerivedRoot(
-                new InMemoryFileSystem(DigestHashFunction.SHA256).getPath("/output"), false, "bin"),
+                new InMemoryFileSystem(DigestHashFunction.SHA256).getPath("/output"),
+                false,
+                false,
+                false,
+                "bin"),
             PathFragment.create("bin/dummy"),
             ActionsTestUtil.NULL_ARTIFACT_OWNER,
             SpecialArtifactType.CONSTANT_METADATA);
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ArtifactFactoryTest.java b/src/test/java/com/google/devtools/build/lib/actions/ArtifactFactoryTest.java
index 5d2fcd8..7dcc53a 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ArtifactFactoryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ArtifactFactoryTest.java
@@ -75,7 +75,7 @@
     clientRoot = Root.fromPath(scratch.dir("/client/workspace"));
     clientRoRoot = Root.fromPath(scratch.dir("/client/RO/workspace"));
     alienRoot = Root.fromPath(scratch.dir("/client/workspace"));
-    outRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "out-root", "x", "bin");
+    outRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out-root", "x", "bin");
 
     fooPath = PathFragment.create("foo");
     fooPackage = PackageIdentifier.createInMainRepo(fooPath);
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ArtifactRootTest.java b/src/test/java/com/google/devtools/build/lib/actions/ArtifactRootTest.java
index 2b0a296..fa98a88 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ArtifactRootTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ArtifactRootTest.java
@@ -57,7 +57,7 @@
     Path execRoot = scratch.dir("/exec");
     Path rootDir = scratch.dir("/exec/root");
 
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, "root");
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "root");
 
     assertThat(root.isSourceRoot()).isFalse();
     assertThat(root.getExecPath()).isEqualTo(PathFragment.create("root"));
@@ -69,14 +69,15 @@
   public void asDerivedRoot_derivedRootIsExecRoot_failsNotOk() throws IOException {
     Path execRoot = scratch.dir("/exec");
     assertThrows(
-        IllegalArgumentException.class, () -> ArtifactRoot.asDerivedRoot(execRoot, false, ""));
+        IllegalArgumentException.class,
+        () -> ArtifactRoot.asDerivedRoot(execRoot, false, false, false, ""));
   }
 
   @Test
   public void asDerivedRoot_emptyPrefix_createsArtifactRoot() throws IOException {
     Path execRoot = scratch.dir("/exec");
-    assertThat(ArtifactRoot.asDerivedRoot(execRoot, false, "", "suffix", ""))
-        .isEqualTo(ArtifactRoot.asDerivedRoot(execRoot, false, "suffix"));
+    assertThat(ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "", "suffix", ""))
+        .isEqualTo(ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "suffix"));
   }
 
   @Test
@@ -84,18 +85,22 @@
     Path execRoot = scratch.dir("/exec");
     assertThrows(
         IllegalArgumentException.class,
-        () -> ArtifactRoot.asDerivedRoot(execRoot, false, "suffix/"));
+        () -> ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "suffix/"));
   }
 
   @Test
   public void asDerivedRoot_noPrefixes_fails() throws IOException {
     Path execRoot = scratch.dir("/exec");
-    assertThrows(IllegalArgumentException.class, () -> ArtifactRoot.asDerivedRoot(execRoot, false));
+    assertThrows(
+        IllegalArgumentException.class,
+        () -> ArtifactRoot.asDerivedRoot(execRoot, false, false, false));
   }
 
   @Test
   public void asDerivedRoot_nullExecPath_fails() {
-    assertThrows(NullPointerException.class, () -> ArtifactRoot.asDerivedRoot(null, false, "exec"));
+    assertThrows(
+        NullPointerException.class,
+        () -> ArtifactRoot.asDerivedRoot(null, false, false, false, "exec"));
   }
 
   @Test
@@ -103,7 +108,8 @@
     Path execRoot = scratch.dir("/exec");
     Path rootDir = scratch.dir("/exec/root");
 
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, PathFragment.create("root"));
+    ArtifactRoot root =
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, PathFragment.create("root"));
 
     assertThat(root.isSourceRoot()).isFalse();
     assertThat(root.getExecPath()).isEqualTo(PathFragment.create("root"));
@@ -117,7 +123,8 @@
     Path rootDir = scratch.dir("/exec/dir1/dir2/dir3");
 
     ArtifactRoot root =
-        ArtifactRoot.asDerivedRoot(execRoot, false, PathFragment.create("dir1/dir2/dir3"));
+        ArtifactRoot.asDerivedRoot(
+            execRoot, false, false, false, PathFragment.create("dir1/dir2/dir3"));
 
     assertThat(root.isSourceRoot()).isFalse();
     assertThat(root.getExecPath()).isEqualTo(PathFragment.create("dir1/dir2/dir3"));
@@ -131,7 +138,8 @@
 
     assertThrows(
         IllegalArgumentException.class,
-        () -> ArtifactRoot.asDerivedRoot(execRoot, false, PathFragment.EMPTY_FRAGMENT));
+        () ->
+            ArtifactRoot.asDerivedRoot(execRoot, false, false, false, PathFragment.EMPTY_FRAGMENT));
   }
 
   @Test
@@ -140,7 +148,7 @@
 
     assertThrows(
         IllegalArgumentException.class,
-        () -> ArtifactRoot.asDerivedRoot(execRoot, false, PathFragment.create(".")));
+        () -> ArtifactRoot.asDerivedRoot(execRoot, false, false, false, PathFragment.create(".")));
   }
 
   @Test
@@ -149,7 +157,7 @@
 
     assertThrows(
         IllegalArgumentException.class,
-        () -> ArtifactRoot.asDerivedRoot(execRoot, false, PathFragment.create("..")));
+        () -> ArtifactRoot.asDerivedRoot(execRoot, false, false, false, PathFragment.create("..")));
   }
 
   @Test
@@ -159,7 +167,8 @@
     assertThrows(
         IllegalArgumentException.class,
         () ->
-            ArtifactRoot.asDerivedRoot(execRoot, false, PathFragment.create("../outsideExecRoot")));
+            ArtifactRoot.asDerivedRoot(
+                execRoot, false, false, false, PathFragment.create("../outsideExecRoot")));
   }
 
   @Test
@@ -167,7 +176,7 @@
       throws Exception {
     Path execRoot = scratch.dir("/thisisaveryverylongexecrootthatwedontwanttoserialize");
     ArtifactRoot derivedRoot =
-        ArtifactRoot.asDerivedRoot(execRoot, false, "first", "second", "third");
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "first", "second", "third");
     ObjectCodecRegistry registry = AutoRegistry.get();
     ImmutableMap<Class<?>, Object> dependencies =
         ImmutableMap.<Class<?>, Object>builder()
@@ -198,10 +207,12 @@
 
     new EqualsTester()
         .addEqualityGroup(
-            ArtifactRoot.asDerivedRoot(execRoot, false, rootSegment),
-            ArtifactRoot.asDerivedRoot(execRoot, false, PathFragment.create(rootSegment)))
-        .addEqualityGroup(ArtifactRoot.asDerivedRoot(otherRootDir, false, "exec", rootSegment))
-        .addEqualityGroup(ArtifactRoot.asDerivedRoot(execRoot, false, "otherSegment"))
+            ArtifactRoot.asDerivedRoot(execRoot, false, false, false, rootSegment),
+            ArtifactRoot.asDerivedRoot(
+                execRoot, false, false, false, PathFragment.create(rootSegment)))
+        .addEqualityGroup(
+            ArtifactRoot.asDerivedRoot(otherRootDir, false, false, false, "exec", rootSegment))
+        .addEqualityGroup(ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "otherSegment"))
         .addEqualityGroup(ArtifactRoot.asSourceRoot(Root.fromPath(sourceDir)))
         .addEqualityGroup(ArtifactRoot.asSourceRoot(Root.fromPath(rootDir)))
         .testEquals();
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java b/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
index b98cea5..6464670 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
@@ -66,7 +66,7 @@
   public final void setRootDir() throws Exception  {
     scratch = new Scratch();
     execDir = scratch.dir("/base/exec");
-    rootDir = ArtifactRoot.asDerivedRoot(execDir, false, "root");
+    rootDir = ArtifactRoot.asDerivedRoot(execDir, false, false, false, "root");
   }
 
   @Test
@@ -76,7 +76,8 @@
         IllegalArgumentException.class,
         () ->
             ActionsTestUtil.createArtifactWithExecPath(
-                    ArtifactRoot.asDerivedRoot(execDir, false, "bogus"), f1.relativeTo(execDir))
+                    ArtifactRoot.asDerivedRoot(execDir, false, false, false, "bogus"),
+                    f1.relativeTo(execDir))
                 .getRootRelativePath());
   }
 
@@ -180,7 +181,8 @@
     Artifact aHeader2 = ActionsTestUtil.createArtifact(root, scratch.file("/foo/bar2.h"));
     Artifact aHeader3 = ActionsTestUtil.createArtifact(root, scratch.file("/foo/bar3.h"));
     ArtifactRoot middleRoot =
-        ArtifactRoot.asDerivedRoot(scratch.dir("/foo"), true, PathFragment.create("/foo/out"));
+        ArtifactRoot.asDerivedRoot(
+            scratch.dir("/foo"), true, false, false, PathFragment.create("/foo/out"));
     Artifact middleman = ActionsTestUtil.createArtifact(middleRoot, "middleman");
     MiddlemanAction.create(
         new ActionRegistry() {
@@ -259,7 +261,8 @@
   public void testToDetailString() throws Exception {
     Path execRoot = scratch.getFileSystem().getPath("/execroot/workspace");
     Artifact a =
-        ActionsTestUtil.createArtifact(ArtifactRoot.asDerivedRoot(execRoot, false, "b"), "c");
+        ActionsTestUtil.createArtifact(
+            ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "b"), "c");
     assertThat(a.toDetailString()).isEqualTo("[[<execution_root>]b]c");
   }
 
@@ -270,7 +273,8 @@
         IllegalArgumentException.class,
         () ->
             ActionsTestUtil.createArtifactWithExecPath(
-                    ArtifactRoot.asDerivedRoot(execRoot, false, "a"), PathFragment.create("c"))
+                    ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "a"),
+                    PathFragment.create("c"))
                 .getRootRelativePath());
   }
 
@@ -280,7 +284,8 @@
         (Artifact.DerivedArtifact) ActionsTestUtil.createArtifact(rootDir, "src/a");
     artifact.setGeneratingActionKey(ActionsTestUtil.NULL_ACTION_LOOKUP_DATA);
     ArtifactRoot anotherRoot =
-        ArtifactRoot.asDerivedRoot(scratch.getFileSystem().getPath("/"), false, "src");
+        ArtifactRoot.asDerivedRoot(
+            scratch.getFileSystem().getPath("/"), false, false, false, "src");
     Artifact.DerivedArtifact anotherArtifact =
         new Artifact.DerivedArtifact(
             anotherRoot,
@@ -382,7 +387,8 @@
         .isTrue();
     assertThat(
             ActionsTestUtil.createArtifact(
-                    ArtifactRoot.asDerivedRoot(scratch.dir("/genfiles"), false, "aaa"),
+                    ArtifactRoot.asDerivedRoot(
+                        scratch.dir("/genfiles"), false, false, false, "aaa"),
                     scratch.file("/genfiles/aaa/bar.out"))
                 .isSourceArtifact())
         .isFalse();
@@ -391,7 +397,7 @@
   @Test
   public void testGetRoot() throws Exception {
     Path execRoot = scratch.getFileSystem().getPath("/");
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, "newRoot");
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "newRoot");
     assertThat(ActionsTestUtil.createArtifact(root, scratch.file("/newRoot/foo")).getRoot())
         .isEqualTo(root);
   }
@@ -399,7 +405,7 @@
   @Test
   public void hashCodeAndEquals() {
     Path execRoot = scratch.getFileSystem().getPath("/");
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, "newRoot");
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "newRoot");
     ActionLookupKey firstOwner =
         new ActionLookupKey() {
           @Override
@@ -465,7 +471,7 @@
   @Test
   public void canDeclareContentBasedOutput() {
     Path execRoot = scratch.getFileSystem().getPath("/");
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, "newRoot");
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "newRoot");
     assertThat(
             new Artifact.DerivedArtifact(
                     root,
@@ -507,7 +513,8 @@
 
   @Test
   public void archivedTreeArtifact_create_returnsArtifactInArchivedRoot() {
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execDir, false, "blaze-out", "fastbuild");
+    ArtifactRoot root =
+        ArtifactRoot.asDerivedRoot(execDir, false, false, false, "blaze-out", "fastbuild");
     SpecialArtifact tree = createTreeArtifact(root, "tree");
 
     ArchivedTreeArtifact archivedTreeArtifact =
@@ -539,7 +546,8 @@
 
   @Test
   public void archivedTreeArtifact_createWithLongerDerivedPrefix_returnsArtifactWithCorrectPath() {
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execDir, false, "dir1", "dir2", "dir3");
+    ArtifactRoot root =
+        ArtifactRoot.asDerivedRoot(execDir, false, false, false, "dir1", "dir2", "dir3");
     SpecialArtifact tree = createTreeArtifact(root, "tree");
 
     ArchivedTreeArtifact archivedTreeArtifact =
@@ -553,7 +561,8 @@
 
   @Test
   public void archivedTreeArtifact_create_failsForWrongDerivedPrefix() {
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execDir, false, "blaze-out", "fastbuild");
+    ArtifactRoot root =
+        ArtifactRoot.asDerivedRoot(execDir, false, false, false, "blaze-out", "fastbuild");
     SpecialArtifact tree = createTreeArtifact(root, "tree");
     PathFragment wrongPrefix = PathFragment.create("notAPrefix");
 
@@ -563,7 +572,7 @@
 
   @Test
   public void archivedTreeArtifact_create_failsForDerivedPrefixOutsideOfArtifactRoot() {
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execDir, false, "dir1", "dir2");
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execDir, false, false, false, "dir1", "dir2");
     SpecialArtifact tree = createTreeArtifact(root, "dir3/tree");
     PathFragment prefixOutsideOfRoot = PathFragment.create("dir1/dir2/dir3");
 
@@ -574,7 +583,8 @@
 
   @Test
   public void archivedTreeArtifact_createWithCustomDerivedTreeRoot_returnsArtifactWithCustomRoot() {
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execDir, false, "blaze-out", "fastbuild");
+    ArtifactRoot root =
+        ArtifactRoot.asDerivedRoot(execDir, false, false, false, "blaze-out", "fastbuild");
     SpecialArtifact tree = createTreeArtifact(root, "dir/tree");
 
     ArchivedTreeArtifact archivedTreeArtifact =
@@ -595,7 +605,8 @@
   public void archivedTreeArtifact_codec_roundTripsArchivedArtifact() throws Exception {
     ArchivedTreeArtifact artifact1 = createArchivedTreeArtifact(rootDir, "tree1");
     ArtifactRoot anotherRoot =
-        ArtifactRoot.asDerivedRoot(scratch.getFileSystem().getPath("/"), false, "src");
+        ArtifactRoot.asDerivedRoot(
+            scratch.getFileSystem().getPath("/"), false, false, false, "src");
     ArchivedTreeArtifact artifact2 = createArchivedTreeArtifact(anotherRoot, "tree2");
     new SerializationTester(artifact1, artifact2)
         .addDependency(FileSystem.class, scratch.getFileSystem())
diff --git a/src/test/java/com/google/devtools/build/lib/actions/CompositeRunfilesSupplierTest.java b/src/test/java/com/google/devtools/build/lib/actions/CompositeRunfilesSupplierTest.java
index 8081f22..9d11441 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/CompositeRunfilesSupplierTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/CompositeRunfilesSupplierTest.java
@@ -48,7 +48,8 @@
   public final void createMocks() throws IOException {
     Scratch scratch = new Scratch();
     execRoot = scratch.getFileSystem().getPath("/");
-    rootDir = ArtifactRoot.asDerivedRoot(execRoot, false, "fake", "root", "dont", "matter");
+    rootDir =
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "fake", "root", "dont", "matter");
 
     mockFirst = mock(RunfilesSupplier.class);
     mockSecond = mock(RunfilesSupplier.class);
diff --git a/src/test/java/com/google/devtools/build/lib/actions/CustomCommandLineTest.java b/src/test/java/com/google/devtools/build/lib/actions/CustomCommandLineTest.java
index 4751466..ced69a9 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/CustomCommandLineTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/CustomCommandLineTest.java
@@ -56,7 +56,7 @@
   @Before
   public void createArtifacts() throws Exception  {
     Scratch scratch = new Scratch();
-    rootDir = ArtifactRoot.asDerivedRoot(scratch.dir("/exec/root"), false, "dir");
+    rootDir = ArtifactRoot.asDerivedRoot(scratch.dir("/exec/root"), false, false, false, "dir");
     artifact1 = ActionsTestUtil.createArtifact(rootDir, scratch.file("/exec/root/dir/file1.txt"));
     artifact2 = ActionsTestUtil.createArtifact(rootDir, scratch.file("/exec/root/dir/file2.txt"));
   }
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java b/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
index beebac2..46d7634 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ExecutableSymlinkActionTest.java
@@ -54,10 +54,11 @@
     final Path inputDir = scratch.dir("/in");
     execRoot = scratch.getFileSystem().getPath("/");
     inputRoot =
-        ArtifactRoot.asDerivedRoot(execRoot, false, inputDir.relativeTo(execRoot).getPathString());
+        ArtifactRoot.asDerivedRoot(
+            execRoot, false, false, false, inputDir.relativeTo(execRoot).getPathString());
     String outSegment = "out";
     execRoot.getChild(outSegment).createDirectoryAndParents();
-    outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, outSegment);
+    outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outSegment);
     outErr = new TestFileOutErr();
     executor = new DummyExecutor(scratch.getFileSystem(), inputDir);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/actions/FailActionTest.java b/src/test/java/com/google/devtools/build/lib/actions/FailActionTest.java
index f697e97..171263e 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/FailActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/FailActionTest.java
@@ -45,7 +45,8 @@
     errorMessage = "An error just happened.";
     anOutput =
         ActionsTestUtil.createArtifact(
-            ArtifactRoot.asDerivedRoot(scratch.dir("/"), false, "out"), scratch.file("/out/foo"));
+            ArtifactRoot.asDerivedRoot(scratch.dir("/"), false, false, false, "out"),
+            scratch.file("/out/foo"));
     outputs = ImmutableList.of(anOutput);
     failAction = new FailAction(NULL_ACTION_OWNER, outputs, errorMessage, Code.FAIL_ACTION_UNKNOWN);
     actionGraph.registerAction(failAction);
diff --git a/src/test/java/com/google/devtools/build/lib/actions/MapBasedActionGraphTest.java b/src/test/java/com/google/devtools/build/lib/actions/MapBasedActionGraphTest.java
index 252b79b..5177fe6 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/MapBasedActionGraphTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/MapBasedActionGraphTest.java
@@ -52,7 +52,7 @@
     Path path = root.getRelative("foo");
     Artifact output =
         ActionsTestUtil.createArtifact(
-            ArtifactRoot.asDerivedRoot(execRoot, false, outSegment), path);
+            ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outSegment), path);
     Action action =
         new TestAction(
             TestAction.NO_EFFECT,
@@ -63,7 +63,7 @@
     path = root.getRelative("bar");
     output =
         ActionsTestUtil.createArtifact(
-            ArtifactRoot.asDerivedRoot(execRoot, false, outSegment), path);
+            ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outSegment), path);
     Action action2 =
         new TestAction(
             TestAction.NO_EFFECT,
@@ -82,7 +82,8 @@
     Path path = root.getRelative("foo");
     Artifact output =
         ActionsTestUtil.createArtifact(
-            ArtifactRoot.asDerivedRoot(execRoot, false, root.relativeTo(execRoot).getPathString()),
+            ArtifactRoot.asDerivedRoot(
+                execRoot, false, false, false, root.relativeTo(execRoot).getPathString()),
             path);
     Action action =
         new TestAction(
@@ -120,7 +121,7 @@
       Path path = root.getChild("foo");
       output =
           ActionsTestUtil.createArtifact(
-              ArtifactRoot.asDerivedRoot(execRoot, false, rootSegment), path);
+              ArtifactRoot.asDerivedRoot(execRoot, false, false, false, rootSegment), path);
       allActions.add(
           new TestAction(
               TestAction.NO_EFFECT,
diff --git a/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java b/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
index 789888a..2774148 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/util/ActionsTestUtil.java
@@ -262,7 +262,7 @@
 
   public static ArtifactRoot createArtifactRootFromTwoPaths(Path root, Path execPath) {
     return ArtifactRoot.asDerivedRoot(
-        root, false, execPath.relativeTo(root).getSegments().toArray(new String[0]));
+        root, false, false, false, execPath.relativeTo(root).getSegments().toArray(new String[0]));
   }
 
   /**
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java
index 4a22984..1e5e631 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/LocationExpanderIntegrationTest.java
@@ -186,6 +186,33 @@
   }
 
   @Test
+  public void otherPathExternalExpansionNoLegacyExternalRunfilesSiblingRepositoryLayout()
+      throws Exception {
+    scratch.file("expansion/BUILD", "sh_library(name='lib', srcs=['@r//p:foo'])");
+    FileSystemUtils.appendIsoLatin1(
+        scratch.resolve("WORKSPACE"), "local_repository(name='r', path='/r')");
+
+    // Invalidate WORKSPACE so @r can be resolved.
+    getSkyframeExecutor()
+        .invalidateFilesUnderPathForTesting(
+            reporter, ModifiedFileSet.EVERYTHING_MODIFIED, Root.fromPath(rootDirectory));
+
+    FileSystemUtils.createDirectoryAndParents(scratch.resolve("/foo/bar"));
+    scratch.file("/r/WORKSPACE", "workspace(name = 'r')");
+    scratch.file("/r/p/BUILD", "genrule(name='foo', outs=['foo.txt'], cmd='never executed')");
+
+    useConfiguration("--nolegacy_external_runfiles");
+    setBuildLanguageOptions("--experimental_sibling_repository_layout");
+    LocationExpander expander = makeExpander("//expansion:lib");
+    assertThat(expander.expand("foo $(execpath @r//p:foo) bar"))
+        .matches("foo .*-out/r/.*/p/foo\\.txt bar");
+    assertThat(expander.expand("foo $(execpaths @r//p:foo) bar"))
+        .matches("foo .*-out/r/.*/p/foo\\.txt bar");
+    assertThat(expander.expand("foo $(rootpath @r//p:foo) bar")).matches("foo ../r/p/foo.txt bar");
+    assertThat(expander.expand("foo $(rootpaths @r//p:foo) bar")).matches("foo ../r/p/foo.txt bar");
+  }
+
+  @Test
   public void otherPathMultiExpansion() throws Exception {
     scratch.file(
         "expansion/BUILD",
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/LocationFunctionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/LocationFunctionTest.java
index 6220fd9..c7e18b3 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/LocationFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/LocationFunctionTest.java
@@ -200,7 +200,8 @@
     FileSystem fs = new InMemoryFileSystem(DigestHashFunction.SHA256);
     if (path.startsWith("/exec/out")) {
       return ActionsTestUtil.createArtifact(
-          ArtifactRoot.asDerivedRoot(fs.getPath("/exec"), false, "out"), fs.getPath(path));
+          ArtifactRoot.asDerivedRoot(fs.getPath("/exec"), false, false, false, "out"),
+          fs.getPath(path));
     } else {
       return ActionsTestUtil.createArtifact(
           ArtifactRoot.asSourceRoot(Root.fromPath(fs.getPath("/exec"))), fs.getPath(path));
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/RunfilesSupplierImplTest.java b/src/test/java/com/google/devtools/build/lib/analysis/RunfilesSupplierImplTest.java
index 40ec2d0..d922820 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/RunfilesSupplierImplTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/RunfilesSupplierImplTest.java
@@ -39,7 +39,8 @@
   public final void setRoot() {
     Scratch scratch = new Scratch();
     Path execRoot = scratch.getFileSystem().getPath("/");
-    rootDir = ArtifactRoot.asDerivedRoot(execRoot, false, "fake", "root", "dont", "matter");
+    rootDir =
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "fake", "root", "dont", "matter");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java b/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java
index 5f181af..811bc42 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/RunfilesTest.java
@@ -158,7 +158,8 @@
 
   @Test
   public void testPutDerivedArtifactWithDifferentOwnerDoesNotConflict() throws Exception {
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(scratch.dir("/workspace"), false, "out");
+    ArtifactRoot root =
+        ArtifactRoot.asDerivedRoot(scratch.dir("/workspace"), false, false, false, "out");
     PathFragment path = PathFragment.create("src/foo.cc");
 
     SimpleActionLookupKey owner1 = new SimpleActionLookupKey("//owner1");
@@ -181,7 +182,8 @@
 
   @Test
   public void testPutDerivedArtifactWithDifferentPathConflicts() throws Exception {
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(scratch.dir("/workspace"), false, "out");
+    ArtifactRoot root =
+        ArtifactRoot.asDerivedRoot(scratch.dir("/workspace"), false, false, false, "out");
     PathFragment path = PathFragment.create("src/foo.cc");
     PathFragment path2 = PathFragment.create("src/bar.cc");
 
@@ -489,7 +491,8 @@
   @Test
   public void testOnlyExtraMiddlemenNotConsideredEmpty() {
     ArtifactRoot root =
-        ArtifactRoot.asDerivedRoot(scratch.resolve("execroot"), true, PathFragment.create("out"));
+        ArtifactRoot.asDerivedRoot(
+            scratch.resolve("execroot"), true, false, false, PathFragment.create("out"));
     Artifact mm = ActionsTestUtil.createArtifact(root, "a-middleman");
     Runfiles runfiles = new Runfiles.Builder("TESTING").addLegacyExtraMiddleman(mm).build();
     assertThat(runfiles.isEmpty()).isFalse();
@@ -498,7 +501,8 @@
   @Test
   public void testMergingExtraMiddlemen() {
     ArtifactRoot root =
-        ArtifactRoot.asDerivedRoot(scratch.resolve("execroot"), true, PathFragment.create("out"));
+        ArtifactRoot.asDerivedRoot(
+            scratch.resolve("execroot"), true, false, false, PathFragment.create("out"));
     Artifact mm1 = ActionsTestUtil.createArtifact(root, "middleman-1");
     Artifact mm2 = ActionsTestUtil.createArtifact(root, "middleman-2");
     Runfiles runfiles1 = new Runfiles.Builder("TESTING").addLegacyExtraMiddleman(mm1).build();
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/SourceManifestActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/SourceManifestActionTest.java
index 112ff7b..e32b4da 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/SourceManifestActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/SourceManifestActionTest.java
@@ -74,7 +74,8 @@
     pythonSourceFile = ActionsTestUtil.createArtifact(trivialRoot, pythonSourcePath);
     fakeManifest.put(buildFilePath.relativeTo(rootDirectory), buildFile);
     fakeManifest.put(pythonSourcePath.relativeTo(rootDirectory), pythonSourceFile);
-    ArtifactRoot outputDir = ArtifactRoot.asDerivedRoot(rootDirectory, false, "blaze-output");
+    ArtifactRoot outputDir =
+        ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, "blaze-output");
     manifestOutputPath = rootDirectory.getRelative("blaze-output/trivial.runfiles_manifest");
     manifestOutputFile = ActionsTestUtil.createArtifact(outputDir, manifestOutputPath);
   }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/TopLevelArtifactHelperTest.java b/src/test/java/com/google/devtools/build/lib/analysis/TopLevelArtifactHelperTest.java
index cbc0507..351d827 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/TopLevelArtifactHelperTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/TopLevelArtifactHelperTest.java
@@ -53,7 +53,7 @@
   public final void setRootDir() throws Exception {
     Scratch scratch = new Scratch();
     Path execRoot = scratch.getFileSystem().getPath("/");
-    root = ArtifactRoot.asDerivedRoot(execRoot, false, "blaze-out");
+    root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "blaze-out");
     path = scratch.dir("/blaze-out/foo");
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/ParamFileWriteActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/ParamFileWriteActionTest.java
index 5bf83bb..aa67978 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/ParamFileWriteActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/ParamFileWriteActionTest.java
@@ -60,7 +60,7 @@
   @Before
   public void createArtifacts() throws Exception  {
     Path execRoot = scratch.getFileSystem().getPath("/exec");
-    rootDir = ArtifactRoot.asDerivedRoot(execRoot, false, "out");
+    rootDir = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out");
     outputArtifact = getBinArtifactWithNoOwner("destination.txt");
     outputArtifact.getPath().getParentDirectory().createDirectoryAndParents();
     treeArtifact = createTreeArtifact("artifact/myTreeFileArtifact");
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplateTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplateTest.java
index dc2b84f..8c8a98f 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplateTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/SpawnActionTemplateTest.java
@@ -54,7 +54,7 @@
   public void setRootDir() throws Exception  {
     Scratch scratch = new Scratch();
     Path execRoot = scratch.getFileSystem().getPath("/");
-    root = ArtifactRoot.asDerivedRoot(execRoot, false, "root");
+    root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "root");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionActionTest.java
index 9f3f003..c80735a 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionActionTest.java
@@ -87,7 +87,7 @@
   private void createArtifacts(String template) throws Exception {
     ArtifactRoot workspace = ArtifactRoot.asSourceRoot(Root.fromPath(scratch.dir("/workspace")));
     scratch.dir("/workspace/out");
-    outputRoot = ArtifactRoot.asDerivedRoot(scratch.dir("/workspace"), false, "out");
+    outputRoot = ArtifactRoot.asDerivedRoot(scratch.dir("/workspace"), false, false, false, "out");
     Path input = scratch.overwriteFile("/workspace/input.txt", StandardCharsets.UTF_8, template);
     inputArtifact = ActionsTestUtil.createArtifact(workspace, input);
     output = scratch.resolve("/workspace/out/destination.txt");
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/starlark/StarlarkCustomCommandLineTest.java b/src/test/java/com/google/devtools/build/lib/analysis/starlark/StarlarkCustomCommandLineTest.java
index 95d1379..4f4e888 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/starlark/StarlarkCustomCommandLineTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/starlark/StarlarkCustomCommandLineTest.java
@@ -59,7 +59,7 @@
   @Before
   public void createArtifactRoot() throws IOException {
     execRoot = scratch.dir("execroot");
-    derivedRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "bin");
+    derivedRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "bin");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java b/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
index 4ca435e..371f19e 100644
--- a/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/exec/SpawnInputExpanderTest.java
@@ -67,7 +67,8 @@
 
   private final FileSystem fs = new InMemoryFileSystem(DigestHashFunction.SHA256);
   private final Path execRoot = fs.getPath("/root");
-  private final ArtifactRoot rootDir = ArtifactRoot.asDerivedRoot(execRoot, false, "out");
+  private final ArtifactRoot rootDir =
+      ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out");
 
   private SpawnInputExpander expander = new SpawnInputExpander(execRoot, /*strict=*/ true);
   private Map<PathFragment, ActionInput> inputMappings = new HashMap<>();
@@ -358,7 +359,8 @@
     Path outputDir = execRoot.getRelative(outputSegment);
     Path outputPath = outputDir.getRelative(relPath);
     outputPath.createDirectoryAndParents();
-    ArtifactRoot derivedRoot = ArtifactRoot.asDerivedRoot(execRoot, false, outputSegment);
+    ArtifactRoot derivedRoot =
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outputSegment);
     return new SpecialArtifact(
         derivedRoot,
         derivedRoot.getExecPath().getRelative(derivedRoot.getRoot().relativize(outputPath)),
diff --git a/src/test/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploaderTest.java b/src/test/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploaderTest.java
index 38c19fa..db77588 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploaderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/ByteStreamBuildEventArtifactUploaderTest.java
@@ -118,7 +118,7 @@
     // on different threads than the setUp.
     prevContext = withEmptyMetadata.attach();
 
-    outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "out");
+    outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out");
     outputRoot.getRoot().asPath().createDirectoryAndParents();
 
     retryService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(1));
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteActionFileSystemTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteActionFileSystemTest.java
index 4dc05e2..c483886 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteActionFileSystemTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteActionFileSystemTest.java
@@ -59,7 +59,7 @@
     MockitoAnnotations.initMocks(this);
     fs = new InMemoryFileSystem(new JavaClock(), HASH_FUNCTION);
     execRoot = fs.getPath("/exec");
-    outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "out");
+    outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out");
     outputRoot.getRoot().asPath().createDirectoryAndParents();
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcherTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcherTest.java
index 446a119..9d44647 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcherTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteActionInputFetcherTest.java
@@ -77,7 +77,7 @@
     Path dev = fs.getPath("/dev");
     dev.createDirectory();
     dev.setWritable(false);
-    artifactRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "root");
+    artifactRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "root");
     artifactRoot.getRoot().asPath().createDirectoryAndParents();
     options = Options.getDefaults(RemoteOptions.class);
     digestUtil = new DigestUtil(HASH_FUNCTION);
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java
index 0e36d31..ec0d2cf 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteCacheTests.java
@@ -113,7 +113,7 @@
     execRoot = fs.getPath("/execroot");
     execRoot.createDirectoryAndParents();
     fakeFileCache = new FakeActionInputFileCache(execRoot);
-    artifactRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "outputs");
+    artifactRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "outputs");
     artifactRoot.getRoot().asPath().createDirectoryAndParents();
     retryService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(1));
   }
diff --git a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
index de37700..ebb5d19 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/RemoteSpawnRunnerTest.java
@@ -966,7 +966,7 @@
     RemoteOptions options = Options.getDefaults(RemoteOptions.class);
     options.remoteOutputsMode = RemoteOutputsMode.TOPLEVEL;
 
-    ArtifactRoot outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "outs");
+    ArtifactRoot outputRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "outs");
     Artifact topLevelOutput =
         ActionsTestUtil.createArtifact(outputRoot, outputRoot.getRoot().getRelative("foo.bin"));
 
diff --git a/src/test/java/com/google/devtools/build/lib/remote/merkletree/DirectoryTreeTest.java b/src/test/java/com/google/devtools/build/lib/remote/merkletree/DirectoryTreeTest.java
index 9a81003..239d220 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/merkletree/DirectoryTreeTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/merkletree/DirectoryTreeTest.java
@@ -48,7 +48,7 @@
   public void setup() {
     FileSystem fs = new InMemoryFileSystem(new JavaClock(), DigestHashFunction.SHA256);
     execRoot = fs.getPath("/exec");
-    artifactRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "srcs");
+    artifactRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "srcs");
     digestUtil = new DigestUtil(fs.getDigestFunction());
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/remote/merkletree/MerkleTreeTest.java b/src/test/java/com/google/devtools/build/lib/remote/merkletree/MerkleTreeTest.java
index 6aa10b0..4b8ba99 100644
--- a/src/test/java/com/google/devtools/build/lib/remote/merkletree/MerkleTreeTest.java
+++ b/src/test/java/com/google/devtools/build/lib/remote/merkletree/MerkleTreeTest.java
@@ -58,7 +58,7 @@
   public void setup() {
     FileSystem fs = new InMemoryFileSystem(new JavaClock(), DigestHashFunction.SHA256);
     execRoot = fs.getPath("/exec");
-    artifactRoot = ArtifactRoot.asDerivedRoot(execRoot, false, "srcs");
+    artifactRoot = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "srcs");
     digestUtil = new DigestUtil(fs.getDigestFunction());
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeaturesTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeaturesTest.java
index 19501cc..a891137 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeaturesTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainFeaturesTest.java
@@ -114,7 +114,7 @@
     Path execRoot = outputBase.getRelative("exec");
     String outSegment = "out";
     Path outputRoot = execRoot.getRelative(outSegment);
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outSegment);
     try {
       return ActionsTestUtil.createArtifact(
           root, scratch.overwriteFile(outputRoot.getRelative(s).toString()));
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLineTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLineTest.java
index c501e91..8c6b7fe 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLineTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CompileCommandLineTest.java
@@ -49,7 +49,7 @@
     Path execRoot = outputBase.getRelative("exec");
     String outSegment = "root";
     Path outputRoot = execRoot.getRelative(outSegment);
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outSegment);
     try {
       return ActionsTestUtil.createArtifact(
           root, scratch.overwriteFile(outputRoot.getRelative(s).toString()));
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java
index 8f01764..b3d8a42 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CppLinkActionTest.java
@@ -700,7 +700,7 @@
     Path execRoot = outputBase.getRelative("exec");
     String outSegment = "out";
     Path outputRoot = execRoot.getRelative(outSegment);
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outSegment);
     try {
       return ActionsTestUtil.createArtifact(
           root, scratch.overwriteFile(outputRoot.getRelative(s).toString()));
@@ -858,7 +858,7 @@
     Path execRoot = fs.getPath(TestUtils.tmpDir());
     PathFragment execPath = PathFragment.create("out").getRelative(name);
     return ActionsTestUtil.createTreeArtifactWithGeneratingAction(
-        ArtifactRoot.asDerivedRoot(execRoot, false, "out"), execPath);
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out"), execPath);
   }
 
   private void verifyArguments(
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CreateIncSymlinkActionTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CreateIncSymlinkActionTest.java
index d540e02..baaa657 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CreateIncSymlinkActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CreateIncSymlinkActionTest.java
@@ -49,7 +49,7 @@
   public void testDifferentOrderSameActionKey() {
     String outSegment = "out";
     Path includePath = rootDirectory.getRelative(outSegment);
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, outSegment);
     Artifact a = ActionsTestUtil.createArtifact(root, "a");
     Artifact b = ActionsTestUtil.createArtifact(root, "b");
     Artifact c = ActionsTestUtil.createArtifact(root, "c");
@@ -71,7 +71,7 @@
   public void testDifferentTargetsDifferentActionKey() {
     String outSegment = "out";
     Path includePath = rootDirectory.getRelative(outSegment);
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, outSegment);
     Artifact a = ActionsTestUtil.createArtifact(root, "a");
     Artifact b = ActionsTestUtil.createArtifact(root, "b");
     CreateIncSymlinkAction action1 =
@@ -89,7 +89,7 @@
   public void testDifferentSymlinksDifferentActionKey() {
     String outSegment = "out";
     Path includePath = rootDirectory.getRelative(outSegment);
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, outSegment);
     Artifact a = ActionsTestUtil.createArtifact(root, "a");
     Artifact b = ActionsTestUtil.createArtifact(root, "b");
     CreateIncSymlinkAction action1 =
@@ -108,7 +108,7 @@
     String outSegment = "out";
     Path outputDir = rootDirectory.getRelative(outSegment);
     outputDir.createDirectory();
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, outSegment);
     Path symlink = rootDirectory.getRelative("out/a");
     Artifact a = ActionsTestUtil.createArtifact(root, symlink);
     Artifact b = ActionsTestUtil.createArtifact(root, "b");
@@ -146,7 +146,7 @@
     String outSegment = "out";
     Path outputDir = rootDirectory.getRelative(outSegment);
     outputDir.createDirectory();
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, outSegment);
     Path symlink = rootDirectory.getRelative("out/subdir/a");
     Artifact a = ActionsTestUtil.createArtifact(root, symlink);
     Artifact b = ActionsTestUtil.createArtifact(root, "b");
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscoveryTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscoveryTest.java
index f6a4c9d..3449acb 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscoveryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/HeaderDiscoveryTest.java
@@ -44,7 +44,7 @@
   private final Path execRoot = fs.getPath("/execroot");
   private final Path derivedRoot = execRoot.getChild(DERIVED_SEGMENT);
   private final ArtifactRoot artifactRoot =
-      ArtifactRoot.asDerivedRoot(execRoot, false, DERIVED_SEGMENT);
+      ArtifactRoot.asDerivedRoot(execRoot, false, false, false, DERIVED_SEGMENT);
 
   @Test
   public void errorsWhenMissingHeaders() {
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java
index 2b350c4..1b59ddc 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/LinkCommandLineTest.java
@@ -58,7 +58,7 @@
     Path execRoot = outputBase.getRelative("exec");
     String outSegment = "root";
     Path outputRoot = execRoot.getRelative(outSegment);
-    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, outSegment);
+    ArtifactRoot root = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outSegment);
     try {
       return ActionsTestUtil.createArtifact(
           root, scratch.overwriteFile(outputRoot.getRelative(s).toString()));
@@ -392,7 +392,7 @@
     Path execRoot = fs.getPath(TestUtils.tmpDir());
     PathFragment execPath = PathFragment.create("out").getRelative(name);
     return ActionsTestUtil.createTreeArtifactWithGeneratingAction(
-        ArtifactRoot.asDerivedRoot(execRoot, false, "out"), execPath);
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out"), execPath);
   }
 
   private void verifyArguments(
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategyTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategyTest.java
index 3fa5b04..951bd5d 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/SpawnGccStrategyTest.java
@@ -51,7 +51,7 @@
   public void setup() {
     fs = new InMemoryFileSystem(new JavaClock(), DigestHashFunction.SHA256);
     execRoot = fs.getPath("/exec/root");
-    ar = ArtifactRoot.asDerivedRoot(execRoot, false, "out");
+    ar = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/rules/objc/J2ObjcSourceTest.java b/src/test/java/com/google/devtools/build/lib/rules/objc/J2ObjcSourceTest.java
index 6393799..348aac8 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/objc/J2ObjcSourceTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/objc/J2ObjcSourceTest.java
@@ -42,7 +42,7 @@
     Path execRoot = scratch.getFileSystem().getPath("/exec");
     String outSegment = "root";
     execRoot.getChild(outSegment).createDirectoryAndParents();
-    rootDir = ArtifactRoot.asDerivedRoot(execRoot, false, outSegment);
+    rootDir = ArtifactRoot.asDerivedRoot(execRoot, false, false, false, outSegment);
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilderTest.java b/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilderTest.java
index 6aa4d67..5418d01 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/proto/ProtoCompileActionBuilderTest.java
@@ -55,7 +55,7 @@
   private final ArtifactRoot root =
       ArtifactRoot.asSourceRoot(Root.fromPath(FILE_SYSTEM.getPath("/")));
   private final ArtifactRoot derivedRoot =
-      ArtifactRoot.asDerivedRoot(FILE_SYSTEM.getPath("/"), false, "out");
+      ArtifactRoot.asDerivedRoot(FILE_SYSTEM.getPath("/"), false, false, false, "out");
 
   private ProtoSource protoSource(String importPath) {
     return protoSource(artifact("//:dont-care", importPath));
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionExecutionValueTransformSharedTreeArtifactsTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionExecutionValueTransformSharedTreeArtifactsTest.java
index 9fd33b3..a9a31ef 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionExecutionValueTransformSharedTreeArtifactsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionExecutionValueTransformSharedTreeArtifactsTest.java
@@ -67,7 +67,9 @@
 
   @Before
   public void createDerivedRoot() throws IOException {
-    derivedRoot = ArtifactRoot.asDerivedRoot(scratch.dir("/execroot"), false, DERIVED_PATH_PREFIX);
+    derivedRoot =
+        ArtifactRoot.asDerivedRoot(
+            scratch.dir("/execroot"), false, false, false, DERIVED_PATH_PREFIX);
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
index 213a871..cabc439 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandlerTest.java
@@ -77,7 +77,8 @@
       ArtifactRoot.asSourceRoot(Root.fromPath(scratch.resolve("/workspace")));
   private final PathFragment derivedPathPrefix = PathFragment.create("bin");
   private final ArtifactRoot outputRoot =
-      ArtifactRoot.asDerivedRoot(scratch.resolve("/output"), false, derivedPathPrefix);
+      ArtifactRoot.asDerivedRoot(
+          scratch.resolve("/output"), false, false, false, derivedPathPrefix);
   private final Path execRoot = outputRoot.getRoot().asPath();
 
   @Before
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunctionTest.java
index 5405870..47de953 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ActionTemplateExpansionFunctionTest.java
@@ -374,7 +374,7 @@
   private SpecialArtifact createTreeArtifact(String path) {
     PathFragment execPath = PathFragment.create("out").getRelative(path);
     return new SpecialArtifact(
-        ArtifactRoot.asDerivedRoot(rootDirectory, false, "out"),
+        ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, "out"),
         execPath,
         CTKEY,
         SpecialArtifactType.TREE);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
index 5764e3b..acc3533 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ArtifactFunctionTest.java
@@ -360,7 +360,8 @@
   private DerivedArtifact createDerivedArtifact(String path) {
     PathFragment execPath = PathFragment.create("out").getRelative(path);
     DerivedArtifact output =
-        new DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, ALL_OWNER);
+        new DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, ALL_OWNER);
     actions.add(new DummyAction(NestedSetBuilder.emptySet(Order.STABLE_ORDER), output));
     output.setGeneratingActionKey(ActionLookupData.create(ALL_OWNER, actions.size() - 1));
     return output;
@@ -368,7 +369,7 @@
 
   private Artifact createMiddlemanArtifact(String path) {
     ArtifactRoot middlemanRoot =
-        ArtifactRoot.asDerivedRoot(middlemanPath, true, PathFragment.create("out"));
+        ArtifactRoot.asDerivedRoot(middlemanPath, true, false, false, PathFragment.create("out"));
     return new DerivedArtifact(
         middlemanRoot, middlemanRoot.getExecPath().getRelative(path), ALL_OWNER);
   }
@@ -383,7 +384,7 @@
   private SpecialArtifact createDerivedTreeArtifactOnly(String path) {
     PathFragment execPath = PathFragment.create("out").getRelative(path);
     return new SpecialArtifact(
-        ArtifactRoot.asDerivedRoot(root, false, "out"),
+        ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
         execPath,
         ALL_OWNER,
         SpecialArtifactType.TREE);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
index b71f1c2..5bd7122 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
@@ -719,7 +719,7 @@
     Path outputPath = fs.getPath("/" + outSegment);
     outputPath.createDirectory();
     return ActionsTestUtil.createArtifact(
-        ArtifactRoot.asDerivedRoot(fs.getPath("/"), false, outSegment),
+        ArtifactRoot.asDerivedRoot(fs.getPath("/"), false, false, false, outSegment),
         outputPath.getRelative(relPath));
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTestBase.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTestBase.java
index 3d62245..78d2592 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTestBase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTestBase.java
@@ -68,7 +68,8 @@
     Path outputDir = fs.getPath("/" + outSegment);
     Path outputPath = outputDir.getRelative(relPath);
     outputDir.createDirectory();
-    ArtifactRoot derivedRoot = ArtifactRoot.asDerivedRoot(fs.getPath("/"), false, outSegment);
+    ArtifactRoot derivedRoot =
+        ArtifactRoot.asDerivedRoot(fs.getPath("/"), false, false, false, outSegment);
     return ActionsTestUtil.createTreeArtifactWithGeneratingAction(
         derivedRoot,
         derivedRoot.getExecPath().getRelative(derivedRoot.getRoot().relativize(outputPath)));
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
index 8c55ff8..2a57175 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunctionTest.java
@@ -213,7 +213,7 @@
 
   private SpecialArtifact treeArtifact(String path) {
     return ActionsTestUtil.createTreeArtifactWithGeneratingAction(
-        ArtifactRoot.asDerivedRoot(rootDirectory, false, "out"),
+        ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, "out"),
         PathFragment.create("out/" + path));
   }
 
@@ -228,7 +228,7 @@
     Artifact.DerivedArtifact result =
         (Artifact.DerivedArtifact)
             ActionsTestUtil.createArtifactWithExecPath(
-                ArtifactRoot.asDerivedRoot(rootDirectory, false, "out"), execPath);
+                ArtifactRoot.asDerivedRoot(rootDirectory, false, false, false, "out"), execPath);
     result.setGeneratingActionKey(
         ActionLookupData.create(ActionsTestUtil.NULL_ARTIFACT_OWNER, artifacts.size()));
     artifacts.add(result);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
index ce26a78..ef8362a 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutorTest.java
@@ -662,14 +662,16 @@
     // an action from its respective configured target.
     ActionLookupKey lc1 = new InjectedActionLookupKey("lc1");
     Artifact output1 =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lc1);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lc1);
     Action action1 =
         new MissingOutputAction(
             NestedSetBuilder.emptySet(Order.STABLE_ORDER), output1, MiddlemanType.NORMAL);
     ConfiguredTargetValue ctValue1 = createConfiguredTargetValue(action1, lc1);
     ActionLookupKey lc2 = new InjectedActionLookupKey("lc2");
     Artifact output2 =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lc2);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lc2);
     Action action2 =
         new MissingOutputAction(
             NestedSetBuilder.emptySet(Order.STABLE_ORDER), output2, MiddlemanType.NORMAL);
@@ -724,7 +726,7 @@
     ActionLookupKey inputKey = new InjectedActionLookupKey("input");
     Artifact input =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             PathFragment.create("out").getRelative("input"),
             inputKey);
     Action baseAction =
@@ -732,14 +734,16 @@
     ConfiguredTargetValue ctBase = createConfiguredTargetValue(baseAction, inputKey);
     ActionLookupKey lc1 = new InjectedActionLookupKey("lc1");
     Artifact output1 =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lc1);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lc1);
     Action action1 =
         new DummyAction(
             NestedSetBuilder.create(Order.STABLE_ORDER, input), output1, MiddlemanType.NORMAL);
     ConfiguredTargetValue ctValue1 = createConfiguredTargetValue(action1, lc1);
     ActionLookupKey lc2 = new InjectedActionLookupKey("lc2");
     Artifact output2 =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lc2);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lc2);
     Action action2 =
         new DummyAction(
             NestedSetBuilder.create(Order.STABLE_ORDER, input), output2, MiddlemanType.NORMAL);
@@ -821,7 +825,8 @@
     // thing if they executed, but they look the same to our execution engine.
     ActionLookupKey lcA = new InjectedActionLookupKey("lcA");
     Artifact outputA =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lcA);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lcA);
     CountDownLatch actionAStartedSoOthersCanProceed = new CountDownLatch(1);
     CountDownLatch actionCFinishedSoACanFinish = new CountDownLatch(1);
     Action actionA =
@@ -845,14 +850,16 @@
     // Shared actions: they look the same from the point of view of Blaze data.
     ActionLookupKey lcB = new InjectedActionLookupKey("lcB");
     Artifact outputB =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lcB);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lcB);
     Action actionB =
         new DummyAction(
             NestedSetBuilder.emptySet(Order.STABLE_ORDER), outputB, MiddlemanType.NORMAL);
     ConfiguredTargetValue ctB = createConfiguredTargetValue(actionB, lcB);
     ActionLookupKey lcC = new InjectedActionLookupKey("lcC");
     Artifact outputC =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lcC);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lcC);
     Action actionC =
         new DummyAction(
             NestedSetBuilder.emptySet(Order.STABLE_ORDER), outputC, MiddlemanType.NORMAL);
@@ -976,7 +983,7 @@
     ActionLookupKey lc1 = new InjectedActionLookupKey("lc1");
     SpecialArtifact output1 =
         new SpecialArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath,
             lc1,
             Artifact.SpecialArtifactType.TREE);
@@ -987,7 +994,7 @@
     ActionLookupKey lc2 = new InjectedActionLookupKey("lc2");
     SpecialArtifact output2 =
         new SpecialArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath,
             lc2,
             Artifact.SpecialArtifactType.TREE);
@@ -1073,7 +1080,7 @@
     ActionLookupKey baseKey = new InjectedActionLookupKey("base");
     SpecialArtifact baseOutput =
         new SpecialArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath,
             baseKey,
             Artifact.SpecialArtifactType.TREE);
@@ -1085,7 +1092,7 @@
     PathFragment execPath2 = PathFragment.create("out").getRelative("treesShared");
     SpecialArtifact sharedOutput1 =
         new SpecialArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath2,
             shared1,
             Artifact.SpecialArtifactType.TREE);
@@ -1095,7 +1102,7 @@
     ActionLookupKey shared2 = new InjectedActionLookupKey("shared2");
     SpecialArtifact sharedOutput2 =
         new SpecialArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath2,
             shared2,
             Artifact.SpecialArtifactType.TREE);
@@ -1266,14 +1273,16 @@
     // an action from its respective configured target.
     ActionLookupKey lc1 = new InjectedActionLookupKey("lc1");
     Artifact output1 =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lc1);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lc1);
     Action action1 =
         new DummyAction(
             NestedSetBuilder.emptySet(Order.STABLE_ORDER), output1, MiddlemanType.NORMAL);
     ConfiguredTargetValue ctValue1 = createConfiguredTargetValue(action1, lc1);
     ActionLookupKey lc2 = new InjectedActionLookupKey("lc2");
     Artifact output2 =
-        new Artifact.DerivedArtifact(ArtifactRoot.asDerivedRoot(root, false, "out"), execPath, lc2);
+        new Artifact.DerivedArtifact(
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"), execPath, lc2);
     CountDownLatch action2Running = new CountDownLatch(1);
     CountDownLatch topActionTestedOutput = new CountDownLatch(1);
     Action action2 =
@@ -1292,7 +1301,9 @@
     ActionLookupKey topLc = new InjectedActionLookupKey("top");
     Artifact top =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"), relativeOut.getChild("top"), topLc);
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
+            relativeOut.getChild("top"),
+            topLc);
     Action topAction =
         new TestAction(
             (Callable<Void> & Serializable)
@@ -1364,7 +1375,9 @@
     ActionLookupKey lc1 = new InjectedActionLookupKey("lc1");
     Artifact output =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"), execPath.getRelative("foo"), lc1);
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
+            execPath.getRelative("foo"),
+            lc1);
     Action action1 = new WarningAction(ImmutableList.of(), output, "action 1");
     SkyValue ctValue1 =
         ValueWithMetadata.normal(
@@ -1377,7 +1390,9 @@
     ActionLookupKey lc2 = new InjectedActionLookupKey("lc2");
     Artifact output2 =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"), execPath.getRelative("bar"), lc2);
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
+            execPath.getRelative("bar"),
+            lc2);
     Action action2 = new WarningAction(ImmutableList.of(output), output2, "action 2");
     SkyValue ctValue2 =
         ValueWithMetadata.normal(
@@ -1537,13 +1552,17 @@
     ActionLookupKey lc1 = new InjectedActionLookupKey("lc1");
     Artifact output =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"), execPath.getRelative("foo"), lc1);
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
+            execPath.getRelative("foo"),
+            lc1);
     Action action1 = new CatastrophicAction(output);
     ConfiguredTargetValue ctValue1 = createConfiguredTargetValue(action1, lc1);
     ActionLookupKey lc2 = new InjectedActionLookupKey("lc2");
     Artifact output2 =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"), execPath.getRelative("bar"), lc2);
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
+            execPath.getRelative("bar"),
+            lc2);
     AtomicBoolean markerRan = new AtomicBoolean(false);
     Action action2 = new MarkerAction(output2, markerRan);
     ConfiguredTargetValue ctValue2 = createConfiguredTargetValue(action2, lc2);
@@ -1633,7 +1652,7 @@
     ActionLookupKey catastropheCTK = new InjectedActionLookupKey("catastrophe");
     Artifact catastropheArtifact =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("zcatas"),
             catastropheCTK);
     CountDownLatch failureHappened = new CountDownLatch(1);
@@ -1652,7 +1671,7 @@
     ActionLookupKey failureCTK = new InjectedActionLookupKey("failure");
     Artifact failureArtifact =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("fail"),
             failureCTK);
     Action failureAction = new FailedExecAction(failureArtifact, USER_DETAILED_EXIT_CODE);
@@ -1660,7 +1679,9 @@
     ActionLookupKey topCTK = new InjectedActionLookupKey("top");
     Artifact topArtifact =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"), execPath.getRelative("top"), topCTK);
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
+            execPath.getRelative("top"),
+            topCTK);
     Action topAction =
         new DummyAction(
             NestedSetBuilder.create(Order.STABLE_ORDER, failureArtifact, catastropheArtifact),
@@ -1744,7 +1765,7 @@
     ActionLookupKey configuredTargetKey = new InjectedActionLookupKey("key");
     Artifact catastropheArtifact =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("catas"),
             configuredTargetKey);
     int failedSize = 100;
@@ -1768,7 +1789,7 @@
       String failString = HashCode.fromBytes(("fail" + i).getBytes(UTF_8)).toString();
       Artifact failureArtifact =
           new Artifact.DerivedArtifact(
-              ArtifactRoot.asDerivedRoot(root, false, "out"),
+              ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
               execPath.getRelative(failString),
               configuredTargetKey);
       failedArtifacts.add(failureArtifact);
@@ -1886,7 +1907,7 @@
     ActionLookupKey failedKey = new InjectedActionLookupKey("failed");
     Artifact failedOutput =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("failed"),
             failedKey);
     AtomicReference<Action> failedActionReference = new AtomicReference<>();
@@ -1911,7 +1932,7 @@
     ActionLookupKey catastrophicKey = new InjectedActionLookupKey("catastrophic");
     Artifact catastrophicOutput =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("catastrophic"),
             catastrophicKey);
     Action catastrophicAction = new CatastrophicAction(catastrophicOutput);
@@ -2007,14 +2028,14 @@
     ActionLookupKey succeededKey = new InjectedActionLookupKey("succeeded");
     Artifact succeededOutput =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("succeeded"),
             succeededKey);
 
     ActionLookupKey failedKey = new InjectedActionLookupKey("failed");
     Artifact failedOutput =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("failed"),
             failedKey);
 
@@ -2093,21 +2114,21 @@
     ActionLookupKey succeededKey = new InjectedActionLookupKey("succeeded");
     Artifact succeededOutput =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("succeeded"),
             succeededKey);
 
     ActionLookupKey failedKey1 = new InjectedActionLookupKey("failed1");
     Artifact failedOutput1 =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("failed1"),
             failedKey1);
 
     ActionLookupKey failedKey2 = new InjectedActionLookupKey("failed2");
     Artifact failedOutput2 =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath.getRelative("failed2"),
             failedKey2);
 
@@ -2194,7 +2215,9 @@
     ActionLookupKey topKey = new InjectedActionLookupKey("top");
     Artifact topOutput =
         new Artifact.DerivedArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"), execPath.getRelative("top"), topKey);
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
+            execPath.getRelative("top"),
+            topKey);
 
     Artifact sourceInput =
         new Artifact.SourceArtifact(
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java b/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
index 2e57668..0f15f09 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TimestampBuilderTestCase.java
@@ -440,7 +440,9 @@
     Path execRoot = fs.getPath(TestUtils.tmpDir());
     PathFragment execPath = PathFragment.create("out").getRelative(name);
     return new Artifact.DerivedArtifact(
-        ArtifactRoot.asDerivedRoot(execRoot, false, "out"), execPath, ACTION_LOOKUP_KEY);
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out"),
+        execPath,
+        ACTION_LOOKUP_KEY);
   }
 
   /** Creates and returns a new "amnesiac" builder based on the amnesiac cache. */
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
index 27ce68f..7521e31 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactBuildTest.java
@@ -966,7 +966,7 @@
         fs.getPath(TestUtils.tmpDir()).getRelative("execroot").getRelative("default-exec-root");
     PathFragment execPath = PathFragment.create("out").getRelative(name);
     return new SpecialArtifact(
-        ArtifactRoot.asDerivedRoot(execRoot, false, "out"),
+        ArtifactRoot.asDerivedRoot(execRoot, false, false, false, "out"),
         execPath,
         ACTION_LOOKUP_KEY,
         SpecialArtifactType.TREE);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
index 31bb25c..9b83081 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactMetadataTest.java
@@ -216,7 +216,7 @@
     Path fullPath = root.getRelative(execPath);
     SpecialArtifact output =
         new SpecialArtifact(
-            ArtifactRoot.asDerivedRoot(root, false, "out"),
+            ArtifactRoot.asDerivedRoot(root, false, false, false, "out"),
             execPath,
             ALL_OWNER,
             SpecialArtifactType.TREE);
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactValueTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactValueTest.java
index 3a77c01..e0e6e0f 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactValueTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/TreeArtifactValueTest.java
@@ -55,7 +55,7 @@
 
   private final Scratch scratch = new Scratch();
   private final ArtifactRoot root =
-      ArtifactRoot.asDerivedRoot(scratch.resolve("root"), false, BIN_PATH);
+      ArtifactRoot.asDerivedRoot(scratch.resolve("root"), false, false, false, BIN_PATH);
 
   @Test
   public void createsCorrectValue() {