Update buildbucket protos.

This will require multiple downstream manual changes, as this includes
the rename of 'builder.proto' to 'builder_common.proto', which will
break both python imports as well as in-proto-file imports.

Bug: 1329911
Recipe-Manual-Change: build
Recipe-Manual-Change: build_limited
Recipe-Manual-Change: chrome_release
Recipe-Manual-Change: chromiumos
Recipe-Manual-Change: fuchsia
Change-Id: I7c5b468337414b2afc85678222922bddad89704a
Reviewed-on: https://chromium-review.googlesource.com/c/infra/luci/recipes-py/+/3761450
Reviewed-by: Yiwei Zhang <yiwzhang@google.com>
Commit-Queue: Robbie Iannucci <iannucci@chromium.org>
diff --git a/README.recipes.md b/README.recipes.md
index abcd6c1..4cfb7c8 100644
--- a/README.recipes.md
+++ b/README.recipes.md
@@ -4289,11 +4289,11 @@
 &mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/add_tags.py#13)(api):**
 ### *recipes* / [buildbucket:tests/build](/recipe_modules/buildbucket/tests/build.py)
 
-[DEPS](/recipe_modules/buildbucket/tests/build.py#21): [assertions](#recipe_modules-assertions), [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
+[DEPS](/recipe_modules/buildbucket/tests/build.py#22): [assertions](#recipe_modules-assertions), [buildbucket](#recipe_modules-buildbucket), [properties](#recipe_modules-properties), [step](#recipe_modules-step)
 
 PYTHON_VERSION_COMPATIBILITY: PY2+3
 
-&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/build.py#29)(api):**
+&mdash; **def [RunSteps](/recipe_modules/buildbucket/tests/build.py#30)(api):**
 ### *recipes* / [buildbucket:tests/cancel](/recipe_modules/buildbucket/tests/cancel.py)
 
 [DEPS](/recipe_modules/buildbucket/tests/cancel.py#12): [buildbucket](#recipe_modules-buildbucket)
diff --git a/recipe_modules/buildbucket/test_api.py b/recipe_modules/buildbucket/test_api.py
index ad18d67..078f3de 100644
--- a/recipe_modules/buildbucket/test_api.py
+++ b/recipe_modules/buildbucket/test_api.py
@@ -12,7 +12,8 @@
 from recipe_engine import recipe_test_api
 
 from PB.go.chromium.org.luci.buildbucket.proto import build as build_pb2
-from PB.go.chromium.org.luci.buildbucket.proto import builder as builder_pb2
+from PB.go.chromium.org.luci.buildbucket.proto \
+  import builder_common as builder_common_pb2
 from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
 from PB.go.chromium.org.luci.buildbucket.proto \
   import builds_service as builds_service_pb2
@@ -82,7 +83,7 @@
         id=build_id,
         number=build_number,
         tags=tags or [],
-        builder=builder_pb2.BuilderID(
+        builder=builder_common_pb2.BuilderID(
             project=project,
             bucket=bucket,
             builder=builder,
@@ -191,7 +192,7 @@
         id=build_id,
         number=build_number,
         tags=tags,
-        builder=builder_pb2.BuilderID(
+        builder=builder_common_pb2.BuilderID(
             project=project,
             bucket=bucket,
             builder=builder,
@@ -257,7 +258,7 @@
         id=build_id,
         number=build_number,
         tags=tags,
-        builder=builder_pb2.BuilderID(
+        builder=builder_common_pb2.BuilderID(
             project=project,
             bucket=bucket,
             builder=builder,
diff --git a/recipe_modules/buildbucket/tests/build.py b/recipe_modules/buildbucket/tests/build.py
index 3b442f2..314947c 100644
--- a/recipe_modules/buildbucket/tests/build.py
+++ b/recipe_modules/buildbucket/tests/build.py
@@ -13,7 +13,8 @@
 from recipe_engine import post_process
 
 from PB.go.chromium.org.luci.buildbucket.proto import build as build_pb2
-from PB.go.chromium.org.luci.buildbucket.proto import builder as builder_pb2
+from PB.go.chromium.org.luci.buildbucket.proto \
+  import builder_common as builder_common_pb2
 from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
 
 PYTHON_VERSION_COMPATIBILITY = 'PY2+3'
@@ -133,7 +134,7 @@
       + api.buildbucket.build(build_pb2.Build(
           id=12484724,
           tags=[],
-          builder=builder_pb2.BuilderID(
+          builder=builder_common_pb2.BuilderID(
               project='test',
               bucket='cron',
               builder='scanner',
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 b6bd056..020be00 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/+/57969079d1b2ed5e0bd4d160da5ab73d833c2dc4/buildbucket/proto
+https://chromium.googlesource.com/infra/luci/luci-go/+/bca9347a514704bb61de08192f560d47738d52f7/buildbucket/proto
diff --git a/recipe_proto/go.chromium.org/luci/buildbucket/proto/build.proto b/recipe_proto/go.chromium.org/luci/buildbucket/proto/build.proto
index b8d3621..19ffdc0 100644
--- a/recipe_proto/go.chromium.org/luci/buildbucket/proto/build.proto
+++ b/recipe_proto/go.chromium.org/luci/buildbucket/proto/build.proto
@@ -22,7 +22,7 @@
 import "google/protobuf/duration.proto";
 import "google/protobuf/timestamp.proto";
 import "google/protobuf/struct.proto";
-import "go.chromium.org/luci/buildbucket/proto/builder.proto";
+import "go.chromium.org/luci/buildbucket/proto/builder_common.proto";
 import "go.chromium.org/luci/buildbucket/proto/common.proto";
 import "go.chromium.org/luci/buildbucket/proto/step.proto";
 import "go.chromium.org/luci/buildbucket/proto/task.proto";
@@ -179,8 +179,30 @@
   // tracked by end_time.
   //
   // During the cancel process, the build still accepts updates.
-  // A delayed task will be executed after the build's grace period to finally
-  // terminate the build.
+  //
+  // bbagent checks this field at the frequency of
+  // buildbucket.MinUpdateBuildInterval. When bbagent sees the build is in
+  // cancel process, there are two states:
+  //  * it has NOT yet started the exe payload,
+  //  * it HAS started the exe payload.
+  //
+  // In the first state, bbagent will immediately terminate the build without
+  // invoking the exe payload at all.
+  //
+  // In the second state, bbagent will send SIGTERM/CTRL-BREAK to the exe
+  // (according to the deadline protocol described in
+  // https://chromium.googlesource.com/infra/luci/luci-py/+/HEAD/client/LUCI_CONTEXT.md).
+  // After grace_period it will then try to kill the exe.
+  //
+  // NOTE: There is a race condition here; If bbagent starts the luciexe and
+  // then immediately notices that the build is canceled, it's possible that
+  // bbagent can send SIGTERM/CTRL-BREAK to the exe before that exe sets up
+  // interrupt handlers. There is a bug on file (crbug.com/1311821)
+  // which we plan to implement at some point as a mitigation for this.
+  //
+  //Additionally, the Buildbucket service itself will launch an asynchronous
+  // task to terminate the build via the backend API (e.g. Swarming cancelation)
+  // if bbagent cannot successfully terminate the exe in time.
   google.protobuf.Timestamp cancel_time = 32;
 
   // Status of the build.
@@ -341,25 +363,7 @@
   // https://chromium.googlesource.com/infra/luci/luci-go/+/0048a84944e872776fba3542aa96d5943ae64bab/common/api/swarming/swarming/v1/swarming-gen.go#1495
   repeated string on_path = 3;
 
-  enum Purpose {
-    PURPOSE_UNSPECIFIED = 0;
-
-    // Can be set to indicate that this DataRef holds a recipe bundle.
-    //
-    // This could be used to clue-in tools like `led edit-recipe-bundle`
-    // that this DataRef is the one holding the recipes. Useful in situations
-    // where the `Build.exe` field is used for a bootstrapper application.
-    PURPOSE_RECIPE_BUNDLE = 1;
-
-    // Can be set to indicate that this DataRef holds data specifically
-    // for bbagent's own use.
-    //
-    // There's a proposal currently to add `nsjail` support to bbagent, and it
-    // would need to bring a copy of `nsjail` in order to run the user binary
-    // but we wouldn't necessarily want to expose it to the user binary.
-    PURPOSE_BBAGENT_UTILITY = 2;
-  }
-  Purpose purpose = 4;
+  reserved 4; // purpose, replaced by agent.data_purposes.
 }
 
 message ResolvedDataRef {
@@ -477,11 +481,44 @@
         // will give the mapping from `agent_platform` to a precise instance_id
         // which was used.
         string agent_platform = 5;
+
+        // Total installation duration for all input data. Currently only record
+        // cipd packages installation time.
+        google.protobuf.Duration total_duration = 6;
       }
 
+      // TODO(crbug.com/1297809): for a long-term solution, we may need to add
+      // a top-level `on_path` array field in the input and read the value from
+      // configuration files (eg.settings.cfg, builder configs). So it can store
+      // the intended order of PATH env var. Then the per-inputDataRef level
+      // `on_path` field will be deprecated.
+      // Currently, the new BBagent flow merges all inputDataRef-level `on_path`
+      // values and sort. This mimics the same behavior of PyBB backend in order
+      // to have the cipd_installation migration to roll out first under a minimal risk.
       Input input = 1;
       Output output = 2;
       Source source = 3;
+
+      enum Purpose {
+        // No categorized/known purpose.
+        PURPOSE_UNSPECIFIED = 0;
+
+        // This path contains the contents of the build's `exe.cipd_package`.
+        PURPOSE_EXE_PAYLOAD = 1;
+
+        // This path contains data specifically for bbagent's own use.
+        //
+        // There's a proposal currently to add `nsjail` support to bbagent, and it
+        // would need to bring a copy of `nsjail` in order to run the user binary
+        // but we wouldn't necessarily want to expose it to the user binary.
+        PURPOSE_BBAGENT_UTILITY = 2;
+      }
+
+      // Maps the relative-to-root directory path in both `input` and `output`
+      // to the Purpose of the software in that directory.
+      //
+      // If a path is not listed here, it is the same as PURPOSE_UNSPECIFIED.
+      map<string, Purpose> purposes = 4;
     }
 
     reserved 4; // field "canary" was moved to Build message.
@@ -745,7 +782,9 @@
     // By default public hosts are accessed anonymously, and the anonymous access
     // has very low quota. Context needs to know all such hostnames in advance to
     // be able to force authenticated access to them.
-    repeated string known_public_gerrit_hosts = 3;
+    //
+    // DEPRECATED: Use build.Infra.Buildbucket.KnownPublicGerritHosts instead.
+    repeated string known_public_gerrit_hosts = 3 [deprecated = true];
 
     // DEPRECATED: Use build.Infra.Buildbucket.Agent.Input instead.
     Input input = 4 [deprecated = true];
diff --git a/recipe_proto/go.chromium.org/luci/buildbucket/proto/builder.proto b/recipe_proto/go.chromium.org/luci/buildbucket/proto/builder_common.proto
similarity index 97%
rename from recipe_proto/go.chromium.org/luci/buildbucket/proto/builder.proto
rename to recipe_proto/go.chromium.org/luci/buildbucket/proto/builder_common.proto
index 7b5703d..6a7a8c4 100644
--- a/recipe_proto/go.chromium.org/luci/buildbucket/proto/builder.proto
+++ b/recipe_proto/go.chromium.org/luci/buildbucket/proto/builder_common.proto
@@ -36,5 +36,5 @@
 
   // User-supplied configuration after normalization.
   // Does not refer to mixins and has defaults inlined.
-  Builder config = 2;
+  BuilderConfig config = 2;
 }
diff --git a/recipe_proto/go.chromium.org/luci/buildbucket/proto/builder_service.proto b/recipe_proto/go.chromium.org/luci/buildbucket/proto/builder_service.proto
index c468c03..7cbb459 100644
--- a/recipe_proto/go.chromium.org/luci/buildbucket/proto/builder_service.proto
+++ b/recipe_proto/go.chromium.org/luci/buildbucket/proto/builder_service.proto
@@ -8,7 +8,7 @@
 
 option go_package = "go.chromium.org/luci/buildbucket/proto;buildbucketpb";
 
-import "go.chromium.org/luci/buildbucket/proto/builder.proto";
+import "go.chromium.org/luci/buildbucket/proto/builder_common.proto";
 
 // Provides preconfigured builders.
 service Builders {
@@ -29,7 +29,7 @@
 message ListBuildersRequest {
   // LUCI project, e.g. "chromium". Omit to list all builders.
   //
-  // Required when bucket is specifed.
+  // Required when bucket is specified.
   string project = 1;
 
   // A bucket in the project, e.g. "try".
diff --git a/recipe_proto/go.chromium.org/luci/buildbucket/proto/builds_service.proto b/recipe_proto/go.chromium.org/luci/buildbucket/proto/builds_service.proto
index d2f13f3..814ab9b 100644
--- a/recipe_proto/go.chromium.org/luci/buildbucket/proto/builds_service.proto
+++ b/recipe_proto/go.chromium.org/luci/buildbucket/proto/builds_service.proto
@@ -13,7 +13,7 @@
 import "google/protobuf/struct.proto";
 import "google/rpc/status.proto";
 import "go.chromium.org/luci/buildbucket/proto/build.proto";
-import "go.chromium.org/luci/buildbucket/proto/builder.proto";
+import "go.chromium.org/luci/buildbucket/proto/builder_common.proto";
 import "go.chromium.org/luci/buildbucket/proto/common.proto";
 import "go.chromium.org/luci/buildbucket/proto/notification.proto";
 import "go.chromium.org/luci/common/proto/structmask/structmask.proto";
@@ -47,7 +47,7 @@
 
   // Updates a build.
   //
-  // RPC metadata must include "X-Build-Token" key with a token
+  // RPC metadata must include "x-buildbucket-token" key with a token
   // generated by the server when scheduling the build.
   rpc UpdateBuild(UpdateBuildRequest) returns (Build) {};
 
@@ -60,13 +60,34 @@
   // The requester must have at least SCHEDULER role in the destination bucket.
   // Note that cancelling a build in ended state (meaning build is not in
   // STATUS_UNSPECIFIED, SCHEDULED or STARTED status) will be a no-op and
-  // directly return up-to-date Build message
+  // directly return up-to-date Build message.
+  //
+  // When called, Buildbucket will set the build's cancelTime to "now".  It
+  // will also recursively start the cancellation process for any children of
+  // this build which are marked as can_outlive_parent=false.
+  //
+  // The next time the build checks in (which happens periodically in
+  // `bbagent`), bbagent will see the cancelTime, and start the cancellation
+  // process described by the 'deadline' section in
+  // https://chromium.googlesource.com/infra/luci/luci-py/+/HEAD/client/LUCI_CONTEXT.md.
+  //
+  // If the build ends before the build's grace_period, then the final status
+  // reported from the build is accepted; this is considered 'graceful termination'.
+  //
+  // If the build doesn't end within the build's grace_period, Buildbucket will
+  // forcibly cancel the build.
   rpc CancelBuild(CancelBuildRequest) returns (Build) {};
 
   // Executes multiple requests in a batch.
   // The response code is always OK.
   // Examples: go/buildbucket-rpc#batch
   rpc Batch(BatchRequest) returns (BatchResponse) {};
+
+  // Creates a new build for the provided build proto.
+  //
+  // If build with the given ID already exists, returns ALREADY_EXISTS
+  // error code.
+  rpc CreateBuild(CreateBuildRequest) returns (Build) {};
 }
 
 // A request message for GetBuild rpc.
@@ -207,6 +228,7 @@
   // - build.summary_markdown
   // - build.tags
   // - build.infra.buildbucket.agent.output
+  // - build.infra.buildbucket.agent.purposes
   //
   // If omitted, Buildbucket will update the Build's update_time, but nothing else.
   google.protobuf.FieldMask update_mask = 2;
@@ -275,10 +297,18 @@
   // replaced with null.
   //
   // Reserved property paths:
-  // * ["buildbucket"]
-  // * ["buildername"]
-  // * ["blamelist""]
-  // * ["$recipe_engine/runtime", "is_experimental"]
+  //   ["$recipe_engine/buildbucket"]
+  //   ["$recipe_engine/runtime", "is_experimental"]
+  //   ["$recipe_engine/runtime", "is_luci"]
+  //   ["branch"]
+  //   ["buildbucket"]
+  //   ["buildername"]
+  //   ["repository"]
+  //
+  // The Builder configuration specifies which top-level property names are
+  // overridable via the `allowed_property_overrides` field. ScheduleBuild
+  // requests which attempt to override a property which isn't allowed will
+  // fail with InvalidArgument.
   //
   // V1 equivalent: corresponds to "properties" key in "parameters_json".
   google.protobuf.Struct properties = 6;
@@ -420,6 +450,11 @@
   // A build that can outlive its parent can also outlive its parent's ancestors.
   //
   // If schedule a build without parent, this field must be UNSET.
+  //
+  // If schedule a build with parent, this field should be YES or NO.
+  // But UNSET is also accepted for now, and it has the same effect as YES.
+  // TODO(crbug.com/1031205): after the parent tracking feature is stable,
+  // require this field to be set when scheduling a build with parent.
   Trinary can_outlive_parent = 21;
 }
 
@@ -443,6 +478,23 @@
   BuildMask mask = 101;
 }
 
+// A request message for CreateBuild rpc.
+message CreateBuildRequest {
+  // The Build to be created.
+  Build build = 1;
+
+  // A unique identifier for this request.
+  // A random UUID is recommended.
+  // This request is only idempotent if a `request_id` is provided.
+  string request_id = 2;
+
+  // What portion of the Build message to return.
+  //
+  // If not set, the default mask is used, see Build message comments for the
+  // list of fields returned by default.
+  BuildMask mask = 3;
+}
+
 // Defines a subset of Build fields and properties to return.
 message BuildMask {
   // Fields of the Build proto to include.
@@ -468,6 +520,19 @@
   //
   // When not empty, implicitly adds the corresponding field to `fields`.
   repeated structmask.StructMask requested_properties = 4;
+
+  // Flag for including all fields.
+  //
+  // Mutually exclusive with `fields`, `input_properties`, `output_properties`,
+  // and `requested_properties`.
+  bool all_fields = 5;
+
+  // A status to filter returned `steps` by. If unspecified, no filter is
+  // applied. Otherwise filters by the union of the given statuses.
+  //
+  // No effect unless `fields` specifies that `steps` should be returned or
+  // `all_fields` is true.
+  repeated Status step_status = 6;
 }
 
 // A build predicate.
diff --git a/recipe_proto/go.chromium.org/luci/buildbucket/proto/project_config.proto b/recipe_proto/go.chromium.org/luci/buildbucket/proto/project_config.proto
index 0faefea..1a542e4 100644
--- a/recipe_proto/go.chromium.org/luci/buildbucket/proto/project_config.proto
+++ b/recipe_proto/go.chromium.org/luci/buildbucket/proto/project_config.proto
@@ -67,17 +67,15 @@
   repeated Acl acls = 2;
 }
 
-
-// Defines a swarmbucket builder or a builder mixin. A builder has a name, a
-// category and specifies what should happen if a build is scheduled to that
-// builder.
+// Defines a swarmbucket builder. A builder has a name, a category and specifies
+// what should happen if a build is scheduled to that builder.
 //
 // SECURITY WARNING: if adding more fields to this message, keep in mind that
 // a user that has permissions to schedule a build to the bucket, can override
 // this config.
 //
-// Next tag: 34.
-message Builder {
+// Next tag: 35.
+message BuilderConfig {
   reserved 8;  // cipd_packages
   reserved 11; // build_numbers of the old format.
   reserved 13; // auto_builder_dimension of the old format.
@@ -130,7 +128,7 @@
     string env_var = 4;
   }
 
-  // DEPRECATED. See Builder.executable and Builder.properties
+  // DEPRECATED. See BuilderConfig.executable and BuilderConfig.properties
   //
   // To specify a recipe name, pass "$recipe_engine" property which is a JSON
   // object having "recipe" property.
@@ -166,7 +164,7 @@
     string cipd_version = 5;
 
     // Colon-separated build properties to set.
-    // Ignored if Builder.properties is set.
+    // Ignored if BuilderConfig.properties is set.
     //
     // Use this field for string properties and use properties_j for other
     // types.
@@ -211,7 +209,7 @@
     string config_json = 2;
   }
 
-  // Name of the builder or builder mixin.
+  // Name of the builder.
   //
   // If a builder name, will be propagated to "builder" build tag and
   // "buildername" recipe property.
@@ -233,8 +231,7 @@
   // Required, but defaults to deprecated Swarming.hostname.
   string swarming_host = 21;
 
-  // DEPRECATED.
-  repeated string mixins = 10;
+  reserved 10; // mixins
 
   // Builder category. Will be used for visual grouping, for example in Code Review.
   string category = 6;
@@ -270,6 +267,19 @@
   // ScheduleBuildRequest.properties.
   string properties = 24 [(luci.text_pb_format) = JSON];
 
+  // A list of top-level property names which can be overridden in
+  // ScheduleBuildRequest.
+  //
+  // If this field is the EXACT value `["*"]` then all properties are permitted
+  // to be overridden.
+  //
+  // NOTE: Some executables (such as the recipe engine) can have drastic
+  // behavior differences based on some properties (for example, the "recipe"
+  // property). If you allow the "recipe" property to be overridden, then anyone
+  // with the 'buildbucket.builds.add' permission could create a Build for this
+  // Builder running a different recipe (from the same recipe repo).
+  repeated string allowed_property_overrides = 34;
+
   // Swarming task priority.
   // A value between 20 and 255, inclusive.
   // Lower means more important.
@@ -361,10 +371,6 @@
   //     auto_builder_dimension: YES
   //   }
   //
-  // We've considered providing interpolation like this
-  //   builder_defaults {
-  //     dimensions: "builder:${BUILDER}"
-  //   }
   // (see also http://docs.buildbot.net/0.8.9/manual/cfg-properties.html#interpolate)
   // but are currently against complicating config with this.
   Toggle auto_builder_dimension = 17;
@@ -438,22 +444,14 @@
 
 // Configuration of buildbucket-swarming integration for one bucket.
 message Swarming {
-  // DEPRECATED. Use builder_defaults.swarming_host instead.
-  // Setting this fields sets builder_defaults.swarming_host.
-  string hostname = 1;
-  // DEPRECATED, IGNORED.
-  // Used to generate a URL for Build, may contain parameters
-  // {swarming_hostname}, {task_id}, {bucket} and {builder}. Defaults to:
-  // https://{swarming_hostname}/user/task/{task_id}
-  string url_format = 2;
-
-  // DEPRECATED.
-  Builder builder_defaults = 3;
+  reserved 1; // hostname
+  reserved 2; // url_format
+  reserved 3; // builder_defaults
 
   // Configuration for each builder.
   // Swarming tasks are created only for builds for builders that are not
   // explicitly specified.
-  repeated Builder builders = 4;
+  repeated BuilderConfig builders = 4;
 
   // DEPRECATED. Use builder_defaults.task_template_canary_percentage instead.
   // Setting this field sets builder_defaults.task_template_canary_percentage.
@@ -474,6 +472,48 @@
   repeated string acl_sets = 4;
   // Buildbucket-swarming integration.
   Swarming swarming = 3;
+
+  // Name of this bucket's shadow bucket for the led builds to use.
+  //
+  // If omitted, it implies that led builds of this bucket reuse this bucket.
+  // This is allowed, but note that it means the led builds will be in
+  // the same bucket/builder with the real builds, which means Any users with
+  // led access will be able to do ANYTHING that this bucket's bots and
+  // service_accounts can do.
+  //
+  // It could also be noisy, such as:
+  // * On the LUCI UI, led builds will show under the same builder as the real builds,
+  // * Led builds will share the same ResultDB config as the real builds, so
+  //   their test results will be exported to the same BigQuery tables.
+  // * Subscribers of Buildbucket PubSub need to filter them out.
+  string shadow = 5;
+
+  // Constraints for a bucket.
+  //
+  // Buildbucket.CreateBuild will validate the incoming requests to make sure
+  // they meet these constraints.
+  message Constraints {
+    // Constraints allowed pools.
+    // Builds in this bucket must have a "pool" dimension which matches an entry in this list.
+    repeated string pools = 1;
+
+    // Only service accounts in this list are allowed.
+    repeated string service_accounts = 2;
+  }
+
+  // Security constraints of the bucket.
+  //
+  // This field is used by CreateBuild on this bucket to constrain proposed
+  // Builds. If a build doesn't meet the constraints, it will be rejected.
+  // For shadow buckets, this is what prevents the bucket from allowing
+  // totally arbitrary Builds.
+  //
+  // `lucicfg` will automatically populate this for the "primary" bucket
+  // when using `luci.builder`.
+  //
+  // Buildbuceket.CreateBuild will validate the incoming requests to make sure
+  // they meet these constraints.
+  Constraints constraints = 6;
 }
 
 // Schema of buildbucket.cfg file, a project config.
@@ -482,8 +522,7 @@
   repeated Bucket buckets = 1;
   // DEPRECATED.
   repeated AclSet acl_sets = 2;
-  // DEPRECATED.
-  repeated Builder builder_mixins = 3;
+  reserved 3; // builder_mixins
 }
 
 // Toggle is a boolean with an extra state UNSET.
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 44440f0..110d74e 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
@@ -49,7 +49,7 @@
 
   // These caches are available to all builders implicitly.
   // A builder may override a cache specified here.
-  repeated Builder.CacheEntry global_caches = 4;
+  repeated BuilderConfig.CacheEntry global_caches = 4;
 
   // CIPD package. Does not specify installation path.
   message Package {
@@ -68,6 +68,14 @@
     // Subdirectory to install the package into, relative to the installation
     // root. Useful if installing two packages at the same root would conflict.
     string subdir = 5;
+
+    // Omit this package from the build having any of these experiments.
+    repeated string omit_on_experiment = 6;
+
+    // If non-empty, include this package only on builds which have any of these
+    // experiments set. `omit_on_experiment` takes precedence if an experiment
+    // is in both of these lists.
+    repeated string include_on_experiment = 7;
   }
 
   // Packages available to the user executable in $PATH.
diff --git a/recipe_proto/go.chromium.org/luci/buildbucket/proto/step.proto b/recipe_proto/go.chromium.org/luci/buildbucket/proto/step.proto
index 25f854f..de3613c 100644
--- a/recipe_proto/go.chromium.org/luci/buildbucket/proto/step.proto
+++ b/recipe_proto/go.chromium.org/luci/buildbucket/proto/step.proto
@@ -62,6 +62,42 @@
   // BigQuery: excluded from rows.
   repeated Log logs = 5;
 
+  message MergeBuild {
+    // If set, then this stream is expected to be a datagram stream
+    // containing Build messages.
+    //
+    // This should be the stream name relative to the current build's
+    // $LOGDOG_NAMESPACE.
+    string from_logdog_stream = 1;
+
+    // If set, then this stream will be merged "in line" with this step.
+    //
+    // Properties emitted by the merge build stream will overwrite global
+    // outputs with the same top-level key.
+    //
+    // Steps emitted by the merge build stream will NOT have their names
+    // namespaced (though the log stream names are still expected to
+    // adhere to the regular luciexe rules).
+    //
+    // Because this is a legacy feature, this intentionally omits other fields
+    // which "could be" merged, because there was no affordance to emit them
+    // under the legacy annotator scheme:
+    //   * output.gitiles_commit will not be merged.
+    //   * output.logs will not be merged.
+    //   * summary_markdown will not be merged.
+    //
+    // This is NOT a recommended mode of operation, but legacy ChromeOS
+    // builders rely on this behavior.
+    //
+    // See crbug.com/1310155.
+    bool legacy_global_namespace = 2;
+  }
+  // MergeBuild is used for go.chromium.org/luci/luciexe to indicate to the
+  // luciexe host process if some Build stream should be merged under this step.
+  //
+  // BigQuery: excluded from rows.
+  MergeBuild merge_build = 6;
+
   // Human-readable summary of the step provided by the step itself,
   // in Markdown format (https://spec.commonmark.org/0.28/).
   //
diff --git a/recipe_proto/go.chromium.org/luci/cv/api/config/v2/README.md b/recipe_proto/go.chromium.org/luci/cv/api/config/v2/README.md
index 6327305..3d3a28b 100644
--- a/recipe_proto/go.chromium.org/luci/cv/api/config/v2/README.md
+++ b/recipe_proto/go.chromium.org/luci/cv/api/config/v2/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/+/55d462f4b57add75b0c2cb0450d44b3c3e083d9f/cv/api/config/v2
+https://chromium.googlesource.com/infra/luci/luci-go/+/9b7759ccc31c79487201c4ac471af58aa48e48ae/cv/api/config/v2
diff --git a/recipe_proto/go.chromium.org/luci/cv/api/config/v2/config.proto b/recipe_proto/go.chromium.org/luci/cv/api/config/v2/config.proto
index 251efb6..8329a1b 100644
--- a/recipe_proto/go.chromium.org/luci/cv/api/config/v2/config.proto
+++ b/recipe_proto/go.chromium.org/luci/cv/api/config/v2/config.proto
@@ -73,7 +73,7 @@
 // ConfigGroup allows one to share single verifiers config across a set of
 // Gerrit repositories, which may be in different Gerrit installations.
 message ConfigGroup {
-  // Next field number: 8.
+  // Next field number: 10.
 
   reserved 3; // allow_cq_depend.
 
@@ -169,16 +169,24 @@
   // mode, CQ will fallback to standard DRY_RUN or FULL_RUN.
   repeated Mode additional_modes = 7;
 
-  // Per-user quota config.
+  // Per-user quota configs for this ConfigGroup.
   //
-  // Runs triggered under this ConfigGroup will be affected by the per-user
-  // quota configs, if there is a QuotaPolicy applicable to a given user.
+  // At the time of a Run creation, CV will look for the QuotaPolicy
+  // for the user in the following sequence.
   //
-  // If not specified, no quota policy will be enforced.
-  UserQuotaConfig user_quota_config = 8;
+  // 1) The QuotaPolicy with "user:<the_gerrit_user>" in principals.
+  // 2) The first QuotaPolicy, where the gerrit user is a member of any of
+  // the groups specified in the principals.
+  // 3) user_quota_default. If unset, all the users are granted unlimited quota.
+  //
+  // Note that CV will pick the first matching policy, not the first policy with
+  // available quotas. For example, if there is a quota policy specifically for
+  // user:foo, then CV will always pick the quota policy when creating a Run,
+  // whether the policy has available quotas or not.
+  repeated QuotaPolicy user_quotas = 8;
+  QuotaPolicy user_quota_default = 9;
 }
 
-
 // SubmitOptions control how CQ submits CLs.
 message SubmitOptions {
   // Optional. Maximum number of successful CQ attempts completed by submitting
@@ -387,7 +395,7 @@
     Toggle cancel_stale_tryjobs = 3 [deprecated = true];
 
     message Builder {
-      // Next field number: 12
+      // Next field number: 16
 
       // Required. Name of the builder as <project>/<bucket>/<builder>
       //
@@ -765,50 +773,50 @@
 
 // QuotaPolicy specifies the quota policy limits.
 message QuotaPolicy {
-    oneof apply_to {
-      // Email of the Gerrit user to apply the quota policy to.
-      string user = 1;
-      // CrIA group to apply the quota policy to.
-      string cria_group = 2;
-    }
-    // Maximum number of active Runs allowed at any given moment.
+    // Name of the policy. Must be unique across all the policies
+    // with the LUCI project.
     //
-    // Active Run is a Run that has started but not ended yet.
-    int32 max_active_runs = 3;
-    // Maximum number of active Tryjobs allowed at any given moment.
+    // Required. Must match regex '^[0-9A-Za-z][0-9A-Za-z\.\-@_+]{0,511}'
+    string name = 1;
+    // Principals to apply the QuotaPolicy to.
     //
-    // Active TryJob is a tryjob that CV has successfully launched, and has not
-    // ended yet from CV's point of view. Also, optional tryjobs, which are
-    // experimental tryjobs that were not explicitly requested via the git
-    // footer, are not counted as active tryjobs.
-    //
-    // Note that counting active tryjobs and launching tryjobs are not atomic
-    // operations. So, # of active tryjobs may not match # of the successfully
-    // launched tryjobs for a short period, but will match eventually.
-    int32 max_active_tryjobs = 4;
-}
+    // Each entry can be either an identity string "user:<email>"
+    // or a LUCI group reference "group:<name>"
+    repeated string principals = 2;
 
-// UserQuotaConfig specifies per-user quota configs.
-message UserQuotaConfig {
-    // A list of QuotaPolicy(s) for Gerrit users.
-    //
-    // * Always pick the first matching QuotaPolicy
-    // If there are more than one applicable QuotaPolicy(s) for a given user,
-    // CV will pick the left-most applicable QuotaPolicy.
-    //
-    // * No QuotaPolicy, no quota enforcement.
-    // If there is no QuotaPolicy applicable to a given Gerrit user, no quota
-    // policy will be enforced, i.e., unlimited quota.
-    repeated QuotaPolicy policies = 2;
-    // Payer tells whose quotas should be consumed for the Run.
-    // If omitted, the CL owner pays the quota charges.
-    enum Payer {
-        // CL owner pays the charged amount.
-        PAYER_CL_OWNER = 0;
-        // Run triggerer, who is the first voter with a CQ label for the Run,
-        // pays the charged amount.
-        PAYER_RUN_TRIGGERER = 1;
+    message Limit {
+      // Required. value must be > 0, unless unlimited is set to True.
+      oneof limit {
+        int64 value = 1;
+        bool unlimited = 2;
+      }
     }
-    Payer payer = 3;
-    bool include_optional = 4;
+
+    message RunLimits {
+        // Maximum number of active runs that this policy can have at any
+        // moment.
+        //
+        // Required. Active Run is a Run that has started but not ended yet.
+        Limit max_active = 1;
+    }
+    message TryjobLimits {
+        // Maximum number of active tryjobs that this policy can have at any
+        // moment.
+        //
+        // Active tryjob is a tryjob that CV has successfully launched,
+        // and has not ended yet from CV's point of view. Also, optional
+        // tryjobs, which are experimental tryjobs that were not explicitly
+        // requested via the git footer, are not counted as active tryjobs.
+        //
+        // Note that counting active tryjobs and launching tryjobs are not
+        // atomic operations. So, # of active tryjobs may not match # of
+        // the successfully launched tryjobs for a short period, but will
+        // match eventually.
+        //
+        // If not specified, an unlimited number of active Tryjobs are allowed.
+        Limit max_active = 1;
+    }
+
+    RunLimits run_limits = 5;
+    TryjobLimits tryjob_limits = 6;
 }
diff --git a/recipe_proto/go.chromium.org/luci/led/job/README.md b/recipe_proto/go.chromium.org/luci/led/job/README.md
index 027fca9..6755639 100644
--- a/recipe_proto/go.chromium.org/luci/led/job/README.md
+++ b/recipe_proto/go.chromium.org/luci/led/job/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/+/0405adb9bb0cb215862d58aec5e100aabb28876b/led/job
+https://chromium.googlesource.com/infra/luci/luci-go/+/e997ca1d1a2b18b5fdbb37cb7be41611059f3eb4/led/job
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 0219d18..c1a154f 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/+/9e9ad4c675d6b9dc4478d299adab41f4f792881e/resultdb/proto
+https://chromium.googlesource.com/infra/luci/luci-go/+/2afc75b3b7af68d2be7928ca0963433b7a9130e9/resultdb/proto
diff --git a/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/predicate.proto b/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/predicate.proto
index 92546d8..966452c 100644
--- a/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/predicate.proto
+++ b/recipe_proto/go.chromium.org/luci/resultdb/proto/v1/predicate.proto
@@ -119,4 +119,8 @@
   // entirely, i.e. the expression is implicitly wrapped with ^ and $.
   // Defaults to ".*".
   string content_type_regexp = 3;
+
+  // An artifact must have an ID matching this regular expression entirely, i.e.
+  // the expression is implicitly wrapped with ^ and $.  Defaults to ".*".
+  string artifact_id_regexp = 4;
 }
diff --git a/recipes/placeholder.proto b/recipes/placeholder.proto
index 6a389f4..2737e53 100644
--- a/recipes/placeholder.proto
+++ b/recipes/placeholder.proto
@@ -6,7 +6,7 @@
 
 import "google/protobuf/struct.proto";
 
-import "go.chromium.org/luci/buildbucket/proto/builder.proto";
+import "go.chromium.org/luci/buildbucket/proto/builder_common.proto";
 import "go.chromium.org/luci/buildbucket/proto/common.proto";
 
 package recipes.recipe_engine.placeholder;
diff --git a/recipes/placeholder.py b/recipes/placeholder.py
index 49b2451..8af3c46 100644
--- a/recipes/placeholder.py
+++ b/recipes/placeholder.py
@@ -19,7 +19,7 @@
 from PB.recipes.recipe_engine.placeholder import (
   InputProps, Step, FakeStep, CollectChildren,
   ChildBuild, Buildbucket, LifeTime)
-from PB.go.chromium.org.luci.buildbucket.proto.builder import BuilderID
+from PB.go.chromium.org.luci.buildbucket.proto.builder_common import BuilderID
 from PB.go.chromium.org.luci.buildbucket.proto.common import Status, ENDED_MASK
 
 from recipe_engine import post_process
@@ -352,4 +352,4 @@
       revision='a' * 40,
       build_number=123,
       experiments=['luci.buildbucket.parent_tracking']
-  )
\ No newline at end of file
+  )