[recipes] Roll recipe protos.

Ran ./update.py

BUG=None

Change-Id: I4c3432ac713eb81c0fcc2748025dbf6c297c2669
Reviewed-on: https://chromium-review.googlesource.com/c/infra/luci/recipes-py/+/5534433
Reviewed-by: Chan Li <chanli@chromium.org>
Auto-Submit: Patrick Meiring <meiring@google.com>
Commit-Queue: Chan Li <chanli@chromium.org>
diff --git a/recipe_proto/go.chromium.org/luci/analysis/proto/README.md b/recipe_proto/go.chromium.org/luci/analysis/proto/README.md
index 1b411d1..563b472 100644
--- a/recipe_proto/go.chromium.org/luci/analysis/proto/README.md
+++ b/recipe_proto/go.chromium.org/luci/analysis/proto/README.md
@@ -1,3 +1,3 @@
 // Generated by update.py. DO NOT EDIT.
 These protos were copied from:
-https://chromium.googlesource.com/infra/luci/luci-go/+/67ab78b154933e5685261a4d8015beaba7bf7bb4/analysis/proto
+https://chromium.googlesource.com/infra/luci/luci-go/+/682fdfd5962f25c000dc9495a5e2a006f7e2fa20/analysis/proto
diff --git a/recipe_proto/go.chromium.org/luci/analysis/proto/bq/test_result_row.proto b/recipe_proto/go.chromium.org/luci/analysis/proto/bq/test_result_row.proto
new file mode 100644
index 0000000..4a80625
--- /dev/null
+++ b/recipe_proto/go.chromium.org/luci/analysis/proto/bq/test_result_row.proto
@@ -0,0 +1,183 @@
+// Copyright 2024 The LUCI Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package luci.analysis.bq;
+
+import "google/protobuf/timestamp.proto";
+
+import "go.chromium.org/luci/analysis/proto/v1/common.proto";
+import "go.chromium.org/luci/analysis/proto/v1/failure_reason.proto";
+import "go.chromium.org/luci/analysis/proto/v1/sources.proto";
+import "go.chromium.org/luci/analysis/proto/v1/test_metadata.proto";
+import "go.chromium.org/luci/analysis/proto/v1/test_verdict.proto";
+import "go.chromium.org/luci/common/bq/pb/options.proto";
+
+option go_package = "go.chromium.org/luci/analysis/proto/bq;bqpb";
+
+// Represents a test result exported to BigQuery.
+//
+// A test result is the outcome of a single execution of a test variant
+// (a way of running a test) in an invocation (a container of test
+// results, such as a build).
+//
+// BigQuery tables using this schema will use the following settings:
+// - Partition by TIMESTAMP_TRUNC(partition_time, DAY),
+//   retain data for 510 days.
+// - Cluster by project, test_id.
+//
+// NextId: 23
+message TestResultRow {
+  // The LUCI Project. E.g. "chromium".
+  string project = 1;
+
+  // Is a unique identifier of the test in a LUCI project.
+  // Refer to TestResult.test_id for details.
+  string test_id = 2;
+
+  // Describes one specific way of running the test,
+  // e.g. a specific bucket, builder and a test suite.
+  //
+  // This will be encoded as a JSON object like
+  // {"builder":"linux-rel","os":"Ubuntu-18.04",...}
+  // to take advantage of BigQuery's JSON support, so that
+  // the query will only be billed for the variant
+  // keys it reads.
+  //
+  // In the protocol buffer, it must be a string as per
+  // https://cloud.google.com/bigquery/docs/write-api#data_type_conversions
+  string variant = 3 [(bqschema.options).bq_type = "JSON"];
+
+  // A hash of the variant, encoded as lowercase hexadecimal characters.
+  // The computation is an implementation detail of ResultDB.
+  string variant_hash = 4;
+
+  message InvocationRecord {
+    // The ID of the invocation.
+    string id = 1;
+
+    // The LUCI Realm the invocation exists under.
+    // For example, "chromium:try".
+    string realm = 2;
+  }
+
+  // Invocation is the ResultDB invocation marked is_export_root
+  // that the test result is being exported under.
+  //
+  // Note: The test result may not have been directly uploaded to
+  // this invocation, but rather one of its included invocations.
+  // See `parent`.
+  InvocationRecord invocation = 5;
+
+  // Partition_time is used to partition the table.
+  // It is the time when exported invocation was created in Spanner.
+  // Note: it is NOT the time when the row is inserted into BigQuery table.
+  // https://cloud.google.com/bigquery/docs/creating-column-partitions#limitations
+  // mentions "The partitioning column must be a top-level field."
+  // So we keep this column here instead of adding the CreateTime to InvocationRecord.
+  google.protobuf.Timestamp partition_time = 6;
+
+  // ParentInvocationRecord for a test result is the immediate parent invocation
+  // that directly contains the test result.
+  message ParentInvocationRecord {
+    // The ID of the invocation.
+    string id = 1;
+
+    // Tags represents Invocation-level string key-value pairs.
+    // A key can be repeated.
+    repeated luci.analysis.v1.StringPair tags = 2;
+
+    // The LUCI Realm the invocation exists under.
+    // For example, "chromium:try".
+    string realm = 3;
+
+    // Arbitrary JSON object that contains structured, domain-specific properties
+    // of the invocation. Stored here stringified as this is the only protocol
+    // buffer type that maps to the JSON BigQuery type:
+    // https://cloud.google.com/bigquery/docs/write-api#data_type_conversions
+    string properties = 4 [(bqschema.options).bq_type = "JSON"];
+  }
+
+  // Parent contains info of the result's immediate parent invocation.
+  ParentInvocationRecord parent = 7;
+
+  // The global identifier of a test result in ResultDB.
+  // Format:
+  // "invocations/{INVOCATION_ID}/tests/{URL_ESCAPED_TEST_ID}/results/{RESULT_ID}".
+  string name = 8;
+
+  // Identifies a test result in a given invocation and test id.
+  string result_id = 9;
+
+  // Expected is a flag indicating whether the result of test case execution is
+  // expected. Refer to TestResult.Expected for details.
+  bool expected = 10;
+
+  // Status of the test result.
+  luci.analysis.v1.TestResultStatus status = 11;
+
+  // A human-readable explanation of the result, in HTML.
+  // MUST be sanitized before rendering in the browser.
+  string summary_html = 12;
+
+  // The point in time when the test case started to execute.
+  google.protobuf.Timestamp start_time = 13;
+
+  // Duration of the test case execution in seconds.
+  double duration_secs = 14;
+
+  // Tags contains metadata for this test result.
+  // It might describe this particular execution or the test case.
+  repeated luci.analysis.v1.StringPair tags = 15;
+
+  // Information about failed tests.
+  // e.g. the assertion failure message.
+  luci.analysis.v1.FailureReason failure_reason = 16;
+
+  // Reasoning behind a test skip, in machine-readable form.
+  // Only set when status is SKIP.
+  // It is the string representation of luci.analysis.v1.SkipReason when
+  // specified and "" when the skip reason is unspecified.
+  string skip_reason = 17;
+
+  // Arbitrary JSON object that contains structured, domain-specific properties
+  // of the test result. Stored here stringified as this is the only protocol
+  // buffer type that maps to the JSON BigQuery type:
+  // https://cloud.google.com/bigquery/docs/write-api#data_type_conversions
+  string properties = 18 [(bqschema.options).bq_type = "JSON"];
+
+  // The code sources tested. Obtained from one of the verdict's test results.
+  // If the invocation which contained the test result
+  // specified that code sources directly, this is those sources.
+  // If the code sources were marked as are inherited from the including
+  // invocation, this is the resolved code sources (if they could be resolved).
+  // Unset otherwise.
+  luci.analysis.v1.Sources sources = 19;
+
+  // The branch in source control that was tested, if known.
+  // For example, the `refs/heads/main` branch in the `chromium/src` repo
+  // hosted by `chromium.googlesource.com`.
+  // This is a subset of the information in the `sources` field.
+  luci.analysis.v1.SourceRef source_ref = 20;
+
+  // Hash of the source_ref field, as 16 lowercase hexadecimal characters.
+  // Can be used to uniquely identify a branch in a source code
+  // version control system.
+  string source_ref_hash = 21;
+
+  // Metadata of the test case,
+  // e.g. the original test name and test location.
+  luci.analysis.v1.TestMetadata test_metadata = 22;
+}
diff --git a/recipe_proto/go.chromium.org/luci/analysis/proto/bq/test_verdict_row.proto b/recipe_proto/go.chromium.org/luci/analysis/proto/bq/test_verdict_row.proto
index 2a0fd73..af897e6 100644
--- a/recipe_proto/go.chromium.org/luci/analysis/proto/bq/test_verdict_row.proto
+++ b/recipe_proto/go.chromium.org/luci/analysis/proto/bq/test_verdict_row.proto
@@ -35,7 +35,7 @@
 //
 // BigQuery tables using this schema will use the following settings:
 // - Partition by TIMESTAMP_TRUNC(partition_time, DAY),
-//   retain data for 540 days.
+//   retain data for 510 days.
 // - Cluster by project, test_id.
 //
 // NextId: 17
diff --git a/recipe_proto/go.chromium.org/luci/analysis/proto/v1/clusters.proto b/recipe_proto/go.chromium.org/luci/analysis/proto/v1/clusters.proto
index 13691cc..5037dd6 100644
--- a/recipe_proto/go.chromium.org/luci/analysis/proto/v1/clusters.proto
+++ b/recipe_proto/go.chromium.org/luci/analysis/proto/v1/clusters.proto
@@ -507,7 +507,7 @@
         // Identity of the presubmit run that contains this test result.
         // This should be unique per "CQ+1"/"CQ+2" attempt on gerrit.
         //
-        // One presumbit run MAY have many ingested invocation IDs (e.g. for its
+        // One presubmit run MAY have many ingested invocation IDs (e.g. for its
         // various tryjobs), but every ingested invocation ID only ever has one
         // presubmit run ID (if any).
         //
diff --git a/recipe_proto/go.chromium.org/luci/analysis/proto/v1/test_variant_branches.proto b/recipe_proto/go.chromium.org/luci/analysis/proto/v1/test_variant_branches.proto
index 817ba00..05f408a 100644
--- a/recipe_proto/go.chromium.org/luci/analysis/proto/v1/test_variant_branches.proto
+++ b/recipe_proto/go.chromium.org/luci/analysis/proto/v1/test_variant_branches.proto
@@ -41,6 +41,9 @@
   // Retrieves the current state of segments of test variant branch analysis in batches.
   rpc BatchGet(BatchGetTestVariantBranchRequest) returns (
     BatchGetTestVariantBranchResponse) {};
+  // Query queries test variant branches for a given test id and ref.
+  rpc Query(QueryTestVariantBranchRequest) returns (
+    QueryTestVariantBranchResponse) {};
   // Lists commits and the test verdicts at these commits, starting from a source position.
   rpc QuerySourcePositions(QuerySourcePositionsRequest) returns (QuerySourcePositionsResponse) {};
 }
@@ -203,6 +206,42 @@
   repeated TestVariantBranch test_variant_branches = 1;
 }
 
+// A request message for `TestVariantBranches.Query` RPC.
+message QueryTestVariantBranchRequest {
+  // Required. The LUCI project of the test variant branch.
+  string project = 1;
+
+  // Required. The test id of test variant branches.
+  string test_id = 2;
+
+  // Required. The source ref of test variant branches.
+  luci.analysis.v1.SourceRef ref = 3;
+
+  // The maximum number of entries to return.
+  //
+  // The service may return fewer than this value.
+  // If unspecified, at most 100 variants will be returned.
+  // The maximum value is 1000; values above 1000 will be coerced to 1000.
+  int32 page_size = 4;
+
+  // A page token, received from a previous call.
+  // Provide this to retrieve the subsequent page.
+  //
+  // When paginating, all other parameters provided to the next call MUST
+  // match the call that provided the page token.
+  string page_token = 5;
+}
+
+message QueryTestVariantBranchResponse {
+  // The list of test variant branches ordered by `variant_hash` ASC.
+  repeated TestVariantBranch test_variant_branch = 1;
+
+  // This field will be set if there are more results to return.
+  // To get the next page of data, send the same request again, but include this
+  // token.
+  string next_page_token = 2;
+}
+
 // Represents changepoint analysis for a particular (project, test, variant, ref).
 message TestVariantBranch {
   // The name of the test variant branch.
diff --git a/recipe_proto/go.chromium.org/luci/buildbucket/proto/README.md b/recipe_proto/go.chromium.org/luci/buildbucket/proto/README.md
index bf04f3e..d88d727 100644
--- a/recipe_proto/go.chromium.org/luci/buildbucket/proto/README.md
+++ b/recipe_proto/go.chromium.org/luci/buildbucket/proto/README.md
@@ -1,3 +1,3 @@
 // Generated by update.py. DO NOT EDIT.
 These protos were copied from:
-https://chromium.googlesource.com/infra/luci/luci-go/+/7712304dc5b2da150ad97181dcd71f31c2280f60/buildbucket/proto
+https://chromium.googlesource.com/infra/luci/luci-go/+/536ac16045acc49762b9f1053abb9e7fa5acd2b1/buildbucket/proto
diff --git a/recipe_proto/go.chromium.org/luci/buildbucket/proto/service_config.proto b/recipe_proto/go.chromium.org/luci/buildbucket/proto/service_config.proto
index 432ef0a..f4c34d9 100644
--- a/recipe_proto/go.chromium.org/luci/buildbucket/proto/service_config.proto
+++ b/recipe_proto/go.chromium.org/luci/buildbucket/proto/service_config.proto
@@ -19,6 +19,7 @@
 package buildbucket;
 
 import "google/protobuf/duration.proto";
+
 import "go.chromium.org/luci/buildbucket/proto/project_config.proto";
 
 option go_package = "go.chromium.org/luci/buildbucket/proto;buildbucketpb";
@@ -49,6 +50,13 @@
   // derive a backend config from swarming related configs.
   // So we could control the migration process behind the scene.
   map<string, string> swarming_backends = 8;
+
+  // Custom metrics.
+  //
+  // Global config to register custom build metrics that more than one LUCI
+  // projects may report data to.
+  // Therefore, the metric names must be unique globally.
+  repeated CustomMetric custom_metrics = 9;
 }
 
 // Backend setting.
@@ -334,3 +342,73 @@
   // Source of the cipd package itself
   Source source = 2;
 }
+
+// A CustomBuildMetricBase tells the base metric of a given custom metric.
+//
+// Buildbucket will determine what to report to and the annotation for
+// a given CustomMetric, based on the specified metric base. Visit
+// http://shortn/_6bc0aXIouD to find all the supported metric bases.
+enum CustomBuildMetricBase {
+  CUSTOM_BUILD_METRIC_BASE_UNSET = 0;
+  CUSTOM_BUILD_METRIC_BASE_CREATED = 1;
+  CUSTOM_BUILD_METRIC_BASE_STARTED = 2;
+  CUSTOM_BUILD_METRIC_BASE_COMPLETED = 3;
+  CUSTOM_BUILD_METRIC_BASE_CYCLE_DURATIONS = 4;
+  CUSTOM_BUILD_METRIC_BASE_RUN_DURATIONS = 5;
+  CUSTOM_BUILD_METRIC_BASE_SCHEDULING_DURATIONS = 6;
+  CUSTOM_BUILD_METRIC_BASE_MAX_AGE_SCHEDULED = 7;
+  CUSTOM_BUILD_METRIC_BASE_COUNT = 8;
+  CUSTOM_BUILD_METRIC_BASE_CONSECUTIVE_FAILURE_COUNT = 9;
+}
+
+// CustomMetric is a metric object that collects a series of data
+// exported by a given metric class.
+//
+// All metrics are using luci.Builder schema at https://source.chromium.org/chromium/infra/infra/+/main:go/src/go.chromium.org/luci/buildbucket/metrics/builder.go;l=26-48;drc=b77d0c2bd4bbb536f8e349e993e3ca18818c51e7.
+//
+// Builders can then reference a custom metric by name to report their build
+// metrics.
+message CustomMetric {
+  // Name of the metric.
+  //
+  // Must be unique globally across all LUCI projects.
+  // Must conform to ^(/[a-zA-Z0-9_-]+)+$ (See
+  // https://source.chromium.org/chromium/infra/infra/+/main:go/src/go.chromium.org/luci/common/tsmon/registry/registry.go;l=33;drc=b77d0c2bd4bbb536f8e349e993e3ca18818c51e7).
+  string name = 1;
+
+  // Description of the metric.
+  string description = 2;
+
+  // Metric field names.
+  //
+  // Each builder uses this metric must specify how to populate the values of
+  // each field in their configurations.
+  //
+  // The value type of all metric fields is always string.
+  // Each Field must conform to ^[A-Za-z_][A-Za-z0-9_]*$ (See
+  // https://source.chromium.org/chromium/infra/infra/+/main:go/src/go.chromium.org/luci/common/tsmon/registry/registry.go;l=34;drc=b77d0c2bd4bbb536f8e349e993e3ca18818c51e7).
+  //
+  // Note that the possible values of each field should be bounded.
+  // So below fields should not be included:
+  // * build id
+  // * any timestamp (e.g. build's creation time)
+  // * any markdown strings (e.g. build's summary_markdown)
+  // * any log strings (e.g. build's output logs)
+  // * any PII
+  // * build's gitiles commit hash
+  // * build's gerrit change number
+  // * etc
+  repeated string fields = 3;
+
+  // True if the metric is disabled.
+  //
+  // This is useful for pausing a metric report pipeline temporary
+  // in an emergency situation.
+  bool disabled = 4;
+
+  // Metric class determines which data to be collected and reported
+  // to the metric.
+  oneof class {
+      CustomBuildMetricBase metric_base = 5;
+  }
+}
diff --git a/recipe_proto/go.chromium.org/luci/common/proto/README.md b/recipe_proto/go.chromium.org/luci/common/proto/README.md
index 03a2191..9301da3 100644
--- a/recipe_proto/go.chromium.org/luci/common/proto/README.md
+++ b/recipe_proto/go.chromium.org/luci/common/proto/README.md
@@ -1,3 +1,3 @@
 // Generated by update.py. DO NOT EDIT.
 These protos were copied from:
-https://chromium.googlesource.com/infra/luci/luci-go/+/48c6a8d100046ff2c4d757b8437e20a82bc49f20/common/proto
+https://chromium.googlesource.com/infra/luci/luci-go/+/2f65c88b328168d7f181be4392cb844ec93252a8/common/proto
diff --git a/recipe_proto/go.chromium.org/luci/gce/api/config/v1/README.md b/recipe_proto/go.chromium.org/luci/gce/api/config/v1/README.md
index 37c25d9..a325c30 100644
--- a/recipe_proto/go.chromium.org/luci/gce/api/config/v1/README.md
+++ b/recipe_proto/go.chromium.org/luci/gce/api/config/v1/README.md
@@ -1,3 +1,3 @@
 // Generated by update.py. DO NOT EDIT.
 These protos were copied from:
-https://chromium.googlesource.com/infra/luci/luci-go/+/952e9f226faf10f2e5a07ab120edb72da333d576/gce/api/config/v1
+https://chromium.googlesource.com/infra/luci/luci-go/+/79437eaa818b8cbe59d46513489191591fc82078/gce/api/config/v1
diff --git a/recipe_proto/go.chromium.org/luci/gce/api/config/v1/config.proto b/recipe_proto/go.chromium.org/luci/gce/api/config/v1/config.proto
index 9abe28a..00ffcd0 100644
--- a/recipe_proto/go.chromium.org/luci/gce/api/config/v1/config.proto
+++ b/recipe_proto/go.chromium.org/luci/gce/api/config/v1/config.proto
@@ -287,6 +287,10 @@
   // The hostname of the Swarming server these VMs should connect to.
   string swarming = 6;
 
+  // The Swarming pools are assosiated with prefix.
+  // Any prefix is expected to be mapped to one Swarming pool.
+  repeated string swarming_pools = 11;
+
   // The timeout of these VMs.
   // If no Swarming bot has connected by the timeout,
   // the VM is deleted and replaced.
diff --git a/recipe_proto/go.chromium.org/luci/resultdb/proto/README.md b/recipe_proto/go.chromium.org/luci/resultdb/proto/README.md
index ebc7ffe..b9d47da 100644
--- a/recipe_proto/go.chromium.org/luci/resultdb/proto/README.md
+++ b/recipe_proto/go.chromium.org/luci/resultdb/proto/README.md
@@ -1,3 +1,3 @@
 // Generated by update.py. DO NOT EDIT.
 These protos were copied from:
-https://chromium.googlesource.com/infra/luci/luci-go/+/7f43881f4ef7ff3189fc5ddc69a0938da8774463/resultdb/proto
+https://chromium.googlesource.com/infra/luci/luci-go/+/7d4b2270d09cc446fb7aa67de93b3ec49bef190d/resultdb/proto
diff --git a/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/invocation.proto b/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/invocation.proto
index 1ea2b32..d751707 100644
--- a/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/invocation.proto
+++ b/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/invocation.proto
@@ -30,7 +30,7 @@
 // buildbucket build, CQ attempt.
 // Composable: can include other invocations, see inclusion.proto.
 //
-// Next id: 22.
+// Next id: 23.
 message Invocation {
   reserved 3; // bool interrupted, crbug.com/1078696.
 
@@ -241,6 +241,31 @@
   // Only useful for a build-level invocation.
   // It contains all instructions of steps which belong to the build.
   Instructions step_instructions = 18;
+
+  // Additional JSON object(s) that contain additional structured data about the
+  // invocation. Unlike `properties` this field is not included (denormalized)
+  // in the test results export, it is only available in the finalized
+  // invocations BigQuery export.
+  //
+  // All google.protobuf.Struct values must contain a field '@type' at the
+  // top-level that defines the fully-qualified name of the source protocol
+  // buffer that was used to populate this field (e.g. some.package.MyMessage).
+  //
+  // ResultDB will not validate the contents with respect to this schema, but
+  // downstream systems may depend on the '@type' field to inform how the
+  // contents are interpreted.
+  //
+  // Each key is limited to 63 characters matching
+  // ^[a-z]([a-z0-9_]{0,61}[a-z0-9])?$.
+  // The size of each value is limited to <= 20,000 bytes.
+  // The total size of the map (as measured by proto.Size())
+  // is limited to <= 100,000 bytes.
+  //
+  // Valid field masks:
+  //  * extended_properties
+  //  * extended_properties.exception_occurances
+  //  * extended_properties.test_script_metrics
+  map<string, google.protobuf.Struct> extended_properties = 22;
 }
 
 // BigQueryExport indicates that results in this invocation should be exported
diff --git a/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/resultdb.proto b/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/resultdb.proto
index 509c387..047be9f 100644
--- a/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/resultdb.proto
+++ b/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/resultdb.proto
@@ -97,7 +97,7 @@
   // Supports invocation inclusions.
   rpc QueryArtifacts(QueryArtifactsRequest) returns (QueryArtifactsResponse) {};
 
-  // Retrieves test variants for a test run. A test run comprises only
+  // Retrieves test verdicts for a test run. A test run comprises only
   // the test results from a single invocation and not its included
   // invocations.
   //
@@ -112,9 +112,9 @@
   //
   // To use, the caller must have `resultdb.testResults.list` permission
   // on the queried invocation.
-  rpc QueryRunTestVariants(QueryRunTestVariantsRequest) returns (QueryRunTestVariantsResponse) {};
+  rpc QueryRunTestVerdicts(QueryRunTestVerdictsRequest) returns (QueryRunTestVerdictsResponse) {};
 
-  // Retrieves test variants from an invocation, recursively.
+  // Retrieves test verdicts from an invocation, recursively.
   // Supports invocation inclusions.
   rpc QueryTestVariants(QueryTestVariantsRequest) returns (QueryTestVariantsResponse) {};
 
@@ -399,7 +399,7 @@
 // A request message for QueryTestVariants RPC.
 // Next id: 9.
 message QueryTestVariantsRequest {
-  // Retrieve test variants included in these invocations, directly or indirectly
+  // Retrieve test verdicts included in these invocations, directly or indirectly
   // (via Invocation.included_invocations).
   //
   // As of April 2024, a maximum of one invocation may be specified.
@@ -469,9 +469,9 @@
   map<string, Sources> sources = 3;
 }
 
-// Request message for QueryRunTestVariants RPC.
-message QueryRunTestVariantsRequest {
-  // Retrieve test variants for the test run represented by this invocation.
+// Request message for QueryRunTestVerdicts RPC.
+message QueryRunTestVerdictsRequest {
+  // Retrieve test verdicts for the test run represented by this invocation.
   // Format: invocations/{INVOCATION_ID}.
   string invocation = 1;
 
@@ -493,29 +493,21 @@
   // the server.
   int32 page_size = 3;
 
-  // A page token, received from a previous `QueryRunTestVariants` call.
+  // A page token, received from a previous `QueryRunTestVerdicts` call.
   // Provide this to retrieve the subsequent page.
   //
   // When paginating, all other parameters provided to
-  // `QueryRunTestVariants` MUST match the call that provided the page
+  // `QueryRunTestVerdicts` MUST match the call that provided the page
   // token.
   string page_token = 4;
 }
 
-// A response message for QueryRunTestVariants RPC.
-message QueryRunTestVariantsResponse {
-  // Test variants for the run.
-  //
-  // Exonerations will be unset, as it is verdict-level concept and not
-  // a test run-level concept.
-  //
-  // Sources will also be unset, as this RPC is only querying a particular
-  // test run and there is no opportunity to resolve inherited test sources.
-  // When used in conjunction with the invocation-ready-for-export pub/sub,
-  // sources can be obtained from that pub/sub instead.
+// A response message for QueryRunTestVerdicts RPC.
+message QueryRunTestVerdictsResponse {
+  // Test verdicts for the run.
   //
   // Ordered by test_id, then variant_hash.
-  repeated TestVariant test_variants = 1;
+  repeated RunTestVerdict run_test_verdicts = 1;
 
   // A token, which can be sent as `page_token` to retrieve the next page.
   // If this field is omitted, there were no subsequent pages at the time of
@@ -536,7 +528,7 @@
   }
 
   // Name of the invocation that the test variants are in.
-   string invocation = 1;
+  string invocation = 1;
 
   // A list of test IDs and variant hashes, identifying the requested test
   // variants. Size is limited to 500. Any request for more than 500 variants
diff --git a/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/test_variant.proto b/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/test_variant.proto
index cc3bcf6..92b745f 100644
--- a/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/test_variant.proto
+++ b/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/test_variant.proto
@@ -24,6 +24,7 @@
 option go_package = "go.chromium.org/luci/resultdb/proto/v1;resultpb";
 
 // Represents a matching test variant with its outcomes.
+// Also known as a test verdict.
 message TestVariant {
   // A unique identifier of the test in a LUCI project.
   // Regex: ^[[::print::]]{1,256}$
@@ -109,3 +110,31 @@
   // A test variant must have this status.
   TestVariantStatus status = 1;
 }
+
+// The outcome of a test variant in a test run (a single invocation,
+// excluding any included invocations).
+message RunTestVerdict {
+  // A unique identifier of the test in a LUCI project.
+  // Regex: ^[[::print::]]{1,256}$
+  //
+  // Refer to TestResult.test_id for details.
+  string test_id = 1;
+
+  // Description of one specific way of running the test,
+  // e.g. a specific bucket, builder and a test suite.
+  Variant variant = 2;
+
+  // Hash of the variant.
+  // hex(sha256(sorted(''.join('%s:%s\n' for k, v in variant.items())))).
+  string variant_hash = 3;
+
+  // Outcomes of the test variant.
+  repeated TestResultBundle results = 4;
+
+  // Information about the test at the time of its execution.
+  //
+  // All test results of the same test variant should report the same test
+  // metadata. This RPC relies on this rule and returns test metadata from
+  // *arbitrary* result of the test variant.
+  TestMetadata test_metadata = 5;
+}