Fix concurrent `TreeArtifactValue` builder to always inject empty tree
artifacts with archived representation.

Make sure we always add a `TreeArtifactValue` if all we set for it is the
archived representation. Currently, that is only true for
`TreeArtifactValue.BasicMultiBuilder`, not
`TreeArtifactValue.ConcurrentMultiBuilder`. Fix the concurrent builder to
inject empty tree artifacts when the archived representation is specified.

Change the `TreeArtifactValue.Builder#setArchivedRepresentation` interface to
match `TreeArtifactValue.MultiBuilder`. Add missing test coverage for setting
archived representation when using the builders.

PiperOrigin-RevId: 329323323
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java
index 6ab2c81..0cf4c7b 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionValue.java
@@ -297,12 +297,11 @@
         .ifPresent(
             archivedRepresentation -> {
               newTree.setArchivedRepresentation(
-                  ArchivedRepresentation.create(
-                      new ArchivedTreeArtifact(
-                          newParent,
-                          archivedRepresentation.archivedTreeFileArtifact().getRoot(),
-                          archivedRepresentation.archivedTreeFileArtifact().getExecPath()),
-                      archivedRepresentation.archivedFileValue()));
+                  new ArchivedTreeArtifact(
+                      newParent,
+                      archivedRepresentation.archivedTreeFileArtifact().getRoot(),
+                      archivedRepresentation.archivedTreeFileArtifact().getExecPath()),
+                  archivedRepresentation.archivedFileValue());
             });
 
     return newTree.build();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
index c6c6aa6..56c9dbb 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
@@ -38,7 +38,6 @@
 import com.google.devtools.build.lib.actions.FilesetOutputSymlink;
 import com.google.devtools.build.lib.actions.cache.DigestUtils;
 import com.google.devtools.build.lib.actions.cache.MetadataHandler;
-import com.google.devtools.build.lib.skyframe.TreeArtifactValue.ArchivedRepresentation;
 import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
 import com.google.devtools.build.lib.vfs.Dirent;
 import com.google.devtools.build.lib.vfs.FileStatus;
@@ -366,9 +365,7 @@
       ArchivedTreeArtifact archivedTreeArtifact =
           ArchivedTreeArtifact.create(parent, derivedPathPrefix);
       tree.setArchivedRepresentation(
-          ArchivedRepresentation.create(
-              archivedTreeArtifact,
-              constructFileArtifactValueFromFilesystem(archivedTreeArtifact)));
+          archivedTreeArtifact, constructFileArtifactValueFromFilesystem(archivedTreeArtifact));
     }
 
     return tree.build();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
index 747df12..78d2bc9 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TreeArtifactValue.java
@@ -21,7 +21,6 @@
 import com.google.auto.value.AutoValue;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedMap;
@@ -419,16 +418,27 @@
       return this;
     }
 
-    public Builder setArchivedRepresentation(ArchivedRepresentation archivedRepresentation) {
+    public Builder setArchivedRepresentation(
+        ArchivedTreeArtifact archivedTreeArtifact, FileArtifactValue metadata) {
+      return setArchivedRepresentation(
+          ArchivedRepresentation.create(archivedTreeArtifact, metadata));
+    }
+
+    private Builder setArchivedRepresentation(ArchivedRepresentation archivedRepresentation) {
       checkState(
           this.archivedRepresentation == null,
           "Tried to add 2 archived representations for: %s",
-          archivedRepresentation);
+          parent);
       checkArgument(
-          archivedRepresentation.archivedTreeFileArtifact().getParent().equals(parent),
+          parent.equals(archivedRepresentation.archivedTreeFileArtifact().getParent()),
           "Cannot add archived representation: %s for a mismatching tree artifact: %s",
           archivedRepresentation,
           parent);
+      checkArgument(
+          !archivedRepresentation.archivedFileValue().equals(FileArtifactValue.OMITTED_FILE_MARKER),
+          "Cannot add archived representation: %s to %s because it has omitted metadata.",
+          archivedRepresentation,
+          parent);
       this.archivedRepresentation = archivedRepresentation;
       return this;
     }
@@ -507,10 +517,13 @@
           archivedRepresentations.putIfAbsent(
               archivedArtifact.getParent(),
               ArchivedRepresentation.create(archivedArtifact, metadata));
-      Preconditions.checkArgument(
+      checkArgument(
           oldValue == null,
           "Tried to add 2 archived representations for %s",
           archivedArtifact.getParent());
+      // We inject entries based on keys in the children map. Make sure a placeholder exists in case
+      // the tree artifact is otherwise empty.
+      children.computeIfAbsent(archivedArtifact.getParent(), ignored -> new ConcurrentHashMap<>());
       return this;
     }