Revert "roller: Support matching branches"

This reverts commit 38d643173f8a1f2c1989e1a19acb6eddbd7f4912.

Reason for revert: breaking all rollers

Original change's description:
> roller: Support matching branches
>
> When triggering off a branch name that appears in both the submodule and
> the top-level project, and when match_triggering_branch is set, roll
> into that branch on the top-level project.
>
> Also remove some earlier code that was trying to do something similar.
>
> Bug: b/509549918
> Change-Id: I9d74e3f420213b02875df823f4b67e0e4bdf674e
> Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/409294

TBR=mohrr@google.com,pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com,afoxley@google.com,android-build-ayeaye@system.gserviceaccount.com

No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: b/509549918
Change-Id: I1a59a65e15515a38949408c5783ad029d78e67a9
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/409675
diff --git a/recipe_modules/checkout/api.py b/recipe_modules/checkout/api.py
index f92dac2..741e078 100644
--- a/recipe_modules/checkout/api.py
+++ b/recipe_modules/checkout/api.py
@@ -737,7 +737,6 @@
         ctx: CheckoutContext,
         remote: str = None,
         branch: str = None,
-        fallback_to_head: bool = True,
     ) -> tuple[Change, ...]:
         bb_input: build_pb.Build.Input = self.m.buildbucket.build.input
         results: list[Change] = []
@@ -792,7 +791,7 @@
                             )
                         )
 
-            if not results and fallback_to_head:
+            if not results:
                 # If not triggered by a gitiles_poller gitiles_commit may be
                 # empty. In that case treat the most recent commit on the
                 # remote as the triggering commit. This is a good assumption
@@ -1744,17 +1743,12 @@
 
         return remote1 in equivalent_remotes.get(remote2, ())
 
-    def triggering_changes(self, options: Options) -> tuple[Change]:
+    def triggering_changes(self, options: Options):
         ctx = CheckoutContext(self.m)
         ctx.options = options
         ctx.changes = []
 
-        return self._change_data(
-            ctx,
-            remote=options.remote,
-            branch=options.branch,
-            fallback_to_head=False,
-        )
+        return self._change_data(ctx, options.remote, options.branch)
 
     def __call__(
         self,
diff --git a/recipe_modules/checkout/tests/git.expected/not_in_gerrit.json b/recipe_modules/checkout/tests/git.expected/not_in_gerrit.json
index cf60d75..c62f88d 100644
--- a/recipe_modules/checkout/tests/git.expected/not_in_gerrit.json
+++ b/recipe_modules/checkout/tests/git.expected/not_in_gerrit.json
@@ -1457,6 +1457,64 @@
     "name": "change data"
   },
   {
+    "cmd": [
+      "git",
+      "ls-remote",
+      "--heads",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "main"
+    ],
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "change data.git ls-remote",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@stdout@h3ll0\trefs/heads/main@@@",
+      "@@@STEP_LOG_END@stdout@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-query",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"params\": {\"q\": \"commit:h3ll0\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "change data.number",
+    "timeout": 30,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": \"1234\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"branch\": \"main\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"project\": \"pigweed\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"q\": \"commit:h3ll0\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "change data.changes",
     "~followup_annotations": [
@@ -1464,6 +1522,14 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "change data.changes.pigweed:1234",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_SUMMARY_TEXT@Change(number=1234, remote='https://pigweed.googlesource.com/pigweed/pigweed', ref='h3ll0', rebase=False, project=None, branch='main', gerrit_name='pigweed', submitted=True, patchset=None, path=None, base=None, base_type=None, is_merge=False, commit_message='', topic=None, current_revision=None, primary=False, submit_requirements={}, uploader=None)@@@"
+    ]
+  },
+  {
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipe_modules/checkout/tests/git.expected/other.json b/recipe_modules/checkout/tests/git.expected/other.json
index 9ca9d0f..f53127e 100644
--- a/recipe_modules/checkout/tests/git.expected/other.json
+++ b/recipe_modules/checkout/tests/git.expected/other.json
@@ -1463,6 +1463,64 @@
     "name": "change data"
   },
   {
+    "cmd": [
+      "git",
+      "ls-remote",
+      "--heads",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "main"
+    ],
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "change data.git ls-remote",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@stdout@h3ll0\trefs/heads/main@@@",
+      "@@@STEP_LOG_END@stdout@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-query",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"params\": {\"q\": \"commit:h3ll0\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "change data.number",
+    "timeout": 30,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": \"1234\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"branch\": \"main\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"project\": \"pigweed\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"q\": \"commit:h3ll0\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "change data.changes",
     "~followup_annotations": [
@@ -1470,6 +1528,14 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "change data.changes.pigweed:1234",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_SUMMARY_TEXT@Change(number=1234, remote='https://pigweed.googlesource.com/pigweed/pigweed', ref='h3ll0', rebase=False, project=None, branch='main', gerrit_name='pigweed', submitted=True, patchset=None, path=None, base=None, base_type=None, is_merge=False, commit_message='', topic=None, current_revision=None, primary=False, submit_requirements={}, uploader=None)@@@"
+    ]
+  },
+  {
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipe_modules/submodule_roll/api.py b/recipe_modules/submodule_roll/api.py
index 4427e5e..581cafe 100644
--- a/recipe_modules/submodule_roll/api.py
+++ b/recipe_modules/submodule_roll/api.py
@@ -207,22 +207,45 @@
                     )
                 )
 
+            is_release_branch = False
             if not submodule.branch:
-                try:
-                    submodule.branch = gitmodules.get(section, 'branch')
-                except configparser.NoOptionError:
-                    submodule.branch = 'main'
+                if (
+                    submodule_entry.release_branch_prefix
+                    and checkout.options.branch
+                    and checkout.options.branch.startswith(
+                        submodule_entry.release_branch_prefix
+                    )
+                ):
+                    submodule.branch = checkout.options.branch
+                    is_release_branch = True
+                else:
+                    try:
+                        submodule.branch = gitmodules.get(section, 'branch')
+                    except configparser.NoOptionError:
+                        submodule.branch = 'main'
 
             submodule.remote = self.m.git_roll_util.normalize_remote(
                 gitmodules.get(section, 'url'),
                 checkout.options.remote,
             )
 
-            new_revision = self.m.git_roll_util.resolve_new_revision(
-                submodule.remote,
-                submodule.branch,
-                checkout.remotes_equivalent,
-            )
+            try:
+                new_revision = self.m.git_roll_util.resolve_new_revision(
+                    submodule.remote,
+                    submodule.branch,
+                    checkout.remotes_equivalent,
+                )
+            except self.m.step.StepFailure:
+                if is_release_branch:
+                    self.m.step.empty(
+                        'skip missing release branch',
+                        step_text=(
+                            f'Branch {submodule.branch} not found in '
+                            f'{submodule.remote}'
+                        ),
+                    )
+                    return []
+                raise
 
             change = self.update_pin(
                 checkout,
diff --git a/recipe_modules/submodule_roll/submodule.proto b/recipe_modules/submodule_roll/submodule.proto
index 5d01475..2a7c8d7 100644
--- a/recipe_modules/submodule_roll/submodule.proto
+++ b/recipe_modules/submodule_roll/submodule.proto
@@ -29,4 +29,8 @@
 
   // Timeout for 'git submodule update'. Default: 10 minutes.
   int32 timeout_sec = 4;
+
+  // Prefix for release branches. If set, builds on release branches will be
+  // skipped if the branch doesn't exist in the downstream repo.
+  string release_branch_prefix = 5;
 }
diff --git a/recipe_modules/submodule_roll/test_api.py b/recipe_modules/submodule_roll/test_api.py
index 4014ef3..8709c1b 100644
--- a/recipe_modules/submodule_roll/test_api.py
+++ b/recipe_modules/submodule_roll/test_api.py
@@ -33,8 +33,14 @@
         path: str,
         name: str | None = None,
         branch: str | None = None,
+        release_branch_prefix: str | None = None,
     ) -> SubmoduleEntry:
-        return SubmoduleEntry(path=path, name=name or path, branch=branch or '')
+        return SubmoduleEntry(
+            path=path,
+            name=name or path,
+            branch=branch or '',
+            release_branch_prefix=release_branch_prefix or '',
+        )
 
     def gitmodules(
         self,
diff --git a/recipe_modules/submodule_roll/tests/full.expected/release_branch.json b/recipe_modules/submodule_roll/tests/full.expected/release_branch.json
new file mode 100644
index 0000000..d9693cd
--- /dev/null
+++ b/recipe_modules/submodule_roll/tests/full.expected/release_branch.json
@@ -0,0 +1,572 @@
+[
+  {
+    "cmd": [],
+    "name": "a1"
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/.gitmodules",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "a1.read .gitmodules",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@[submodule \"a1\"]@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@\tpath = a1@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@\turl = sso://foo/a1@@@",
+      "@@@STEP_LOG_END@.gitmodules@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "ls-remote",
+      "--heads",
+      "https://foo.googlesource.com/a1",
+      "release_20260430"
+    ],
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "a1.git ls-remote",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@stdout@h3ll0\trefs/heads/release_20260430@@@",
+      "@@@STEP_LOG_END@stdout@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--jobs",
+      "4",
+      "[START_DIR]/checkout/a1"
+    ],
+    "cwd": "[START_DIR]/checkout",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "a1.git submodule update",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--max-count=1",
+      "--pretty=format:%H"
+    ],
+    "cwd": "[START_DIR]/checkout/a1",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "a1.get old revision",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@1111111111111111111111111111111111111111@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.ensure infra/tools/luci/gitiles/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.ensure infra/tools/luci/gitiles/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "RECIPE_MODULE[fuchsia::gitiles]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "a1.ensure infra/tools/luci/gitiles/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gitiles/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "infra_step": true,
+    "name": "a1.ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gitiles/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "a1.ensure infra/tools/luci/gitiles/${platform}.install infra/tools/luci/gitiles.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gitiles/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gitiles/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gitiles",
+      "log",
+      "-json-output",
+      "/path/to/tmp/json",
+      "-limit",
+      "500",
+      "https://foo.googlesource.com/a1",
+      "1111111111111111111111111111111111111111..h3ll0"
+    ],
+    "infra_step": true,
+    "name": "a1.log a1",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_0.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_0.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 0\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"83a7614b3b60951511be50db1b9561daff4bb447\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"1b6412b24ec3add84836c8fdd1af5ac8e35b61d9\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"8bea05ad53680fce6937543f0d98cd48e295b8ff\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"a.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_1.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_1.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 1\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"8675a52c73c701cb0b2c48f5ed4a9058c624e6cd\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"a1b1e6aa501915989b45a95e1224ec2a88655eb3\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"06bc4c79002f278528aaddae4e056a11f58c19ad\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"b.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_2.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"email\": \"fake_A@fake_2.email.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"Fake A\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"id\": \"363caa907186de786cb5292cd1ab7245da954815\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"message\": \"fake A msg 2\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"255c6325c4c654e17e6b35142e3912c86f1718f2\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree\": \"e84d4ad259e69da73d2b842e2b9709f08e8b22bd\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tree_diff\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_id\": \"d7f478bf423219f2f47c1a6ed344fc597a8bf18f\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_mode\": 33188,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"new_path\": \"c.py\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_id\": \"0000000000000000000000000000000000000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"old_mode\": 0,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"type\": \"add\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.get gerrit change numbers for a1",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.get gerrit change numbers for a1.old revision",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_SUMMARY_TEXT@255c6325c4c654e17e6b35142e3912c86f1718f2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.get gerrit change numbers for a1.new revision",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_SUMMARY_TEXT@3e30158f2a7caccb7a9f6632a60011e7a44e1e5c@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.get gerrit change numbers for a1.3 commits",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.get gerrit change numbers for a1.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.get gerrit change numbers for a1.ensure infra/tools/luci/gerrit/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "RECIPE_MODULE[fuchsia::gerrit]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "deadline": {
+        "grace_period": 30.0,
+        "soft_deadline": 1337000021.5
+      }
+    },
+    "name": "a1.get gerrit change numbers for a1.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.get gerrit change numbers for a1.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "deadline": {
+        "grace_period": 30.0,
+        "soft_deadline": 1337000021.5
+      }
+    },
+    "name": "a1.get gerrit change numbers for a1.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gerrit/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "deadline": {
+        "grace_period": 30.0,
+        "soft_deadline": 1337000021.5
+      }
+    },
+    "name": "a1.get gerrit change numbers for a1.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gerrit/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://foo-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "a1.get gerrit change numbers for a1.change details for 3e30158:https://foo.googlesource.com/a1/+/3e30158f2a7caccb7a9f6632a60011e7a44e1e5c",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"3e30158f2a7caccb7a9f6632a60011e7a44e1e5c\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://foo-review.googlesource.com/q/3e30158f2a7caccb7a9f6632a60011e7a44e1e5c@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://foo-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "a1.get gerrit change numbers for a1.change details for 3380b83:https://foo.googlesource.com/a1/+/3380b83c11e029b7291c83c44e7b1ce09d465fd1",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"3380b83c11e029b7291c83c44e7b1ce09d465fd1\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://foo-review.googlesource.com/q/3380b83c11e029b7291c83c44e7b1ce09d465fd1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://foo-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"363caa907186de786cb5292cd1ab7245da954815\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "a1.get gerrit change numbers for a1.change details for 363caa9:https://foo.googlesource.com/a1/+/363caa907186de786cb5292cd1ab7245da954815",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 12345@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"363caa907186de786cb5292cd1ab7245da954815\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://foo-review.googlesource.com/q/363caa907186de786cb5292cd1ab7245da954815@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "origin",
+      "h3ll0"
+    ],
+    "cwd": "[START_DIR]/checkout/a1",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "a1.git fetch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/checkout/a1",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "a1.git checkout",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "commit message",
+    "~followup_annotations": [
+      "@@@STEP_SUMMARY_TEXT@roll: a1 255c632..3e30158 (3 commits)\n\n3e30158:https://foo-review.googlesource.com/c/a1/+/12345 fake A msg 0\n3380b83:https://foo-review.googlesource.com/c/a1/+/12345 fake A msg 1\n363caa9:https://foo-review.googlesource.com/c/a1/+/12345 fake A msg 2\n\nRolled-Repo: https://foo.googlesource.com/a1\nRolled-Commits: 255c6325c4c654..3e30158f2a7cac@@@"
+    ]
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/submodule_roll/tests/full.expected/release_branch_missing.json b/recipe_modules/submodule_roll/tests/full.expected/release_branch_missing.json
new file mode 100644
index 0000000..2a73252
--- /dev/null
+++ b/recipe_modules/submodule_roll/tests/full.expected/release_branch_missing.json
@@ -0,0 +1,70 @@
+[
+  {
+    "cmd": [],
+    "name": "a1",
+    "~followup_annotations": [
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/.gitmodules",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "a1.read .gitmodules",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@[submodule \"a1\"]@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@\tpath = a1@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@\turl = sso://foo/a1@@@",
+      "@@@STEP_LOG_END@.gitmodules@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "ls-remote",
+      "--heads",
+      "https://foo.googlesource.com/a1",
+      "release_20260430"
+    ],
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "a1.git ls-remote",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "a1.skip missing release branch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_TEXT@Branch release_20260430 not found in https://foo.googlesource.com/a1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "nothing to roll, exiting"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/submodule_roll/tests/full.expected/resolve_failure.json b/recipe_modules/submodule_roll/tests/full.expected/resolve_failure.json
new file mode 100644
index 0000000..8e18593
--- /dev/null
+++ b/recipe_modules/submodule_roll/tests/full.expected/resolve_failure.json
@@ -0,0 +1,62 @@
+[
+  {
+    "cmd": [],
+    "name": "a1",
+    "~followup_annotations": [
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/.gitmodules",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "name": "a1.read .gitmodules",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@[submodule \"a1\"]@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@\tpath = a1@@@",
+      "@@@STEP_LOG_LINE@.gitmodules@\turl = sso://foo/a1@@@",
+      "@@@STEP_LOG_END@.gitmodules@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "ls-remote",
+      "--heads",
+      "https://foo.googlesource.com/a1",
+      "main"
+    ],
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "name": "a1.git ls-remote",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "failure": {
+      "failure": {},
+      "humanReason": "Step('a1.git ls-remote') (retcode: 1)"
+    },
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipe_modules/submodule_roll/tests/full.py b/recipe_modules/submodule_roll/tests/full.py
index 9f0a510..6100d6a 100644
--- a/recipe_modules/submodule_roll/tests/full.py
+++ b/recipe_modules/submodule_roll/tests/full.py
@@ -35,6 +35,8 @@
 message InputProperties {
   // Submodules to update.
   recipe_modules.pigweed.submodule_roll.SubmoduleEntry submodule = 1;
+
+  string branch = 2;
 }
 """
 
@@ -46,6 +48,8 @@
     props: InputProperties,
 ):
     checkout = api.checkout.fake_context()
+    if props.branch:
+        checkout.options.branch = props.branch
 
     rolls = api.submodule_roll.update(checkout, props.submodule)
 
@@ -94,3 +98,40 @@
         api.submodule_roll.gitmodules('b2', a1='sso://foo/a1'),
         status='FAILURE',
     )
+
+    yield api.test(
+        'release_branch',
+        properties(
+            api.submodule_roll.entry('a1', release_branch_prefix='release_'),
+            branch='release_20260430',
+        ),
+        api.submodule_roll.gitmodules(
+            'a1',
+            a1='sso://foo/a1',
+        ),
+        api.gitiles.log('a1.log a1', 'A'),
+    )
+
+    yield api.test(
+        'release_branch_missing',
+        properties(
+            api.submodule_roll.entry('a1', release_branch_prefix='release_'),
+            branch='release_20260430',
+        ),
+        api.submodule_roll.gitmodules(
+            'a1',
+            a1='sso://foo/a1',
+        ),
+        api.step_data('a1.git ls-remote', retcode=1),
+    )
+
+    yield api.test(
+        'resolve_failure',
+        properties(api.submodule_roll.entry('a1')),
+        api.submodule_roll.gitmodules(
+            'a1',
+            a1='sso://foo/a1',
+        ),
+        api.step_data('a1.git ls-remote', retcode=1),
+        status='FAILURE',
+    )
diff --git a/recipes/roller.expected/release_branch_trigger.json b/recipes/roller.expected/release_branch_trigger.json
new file mode 100644
index 0000000..05d9d43
--- /dev/null
+++ b/recipes/roller.expected/release_branch_trigger.json
@@ -0,0 +1,3363 @@
+[
+  {
+    "cmd": [],
+    "name": "abandon old changes"
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "RECIPE_MODULE[fuchsia::gerrit]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07",
+      "-ensure-file",
+      "infra/tools/luci/gerrit/${platform} version:pinned-version",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "abandon old changes.pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-version:pinned-v\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gerrit/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-query",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"params\": {\"q\": \"is:open owner:None age:30d \"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "abandon old changes.pigweed.get old changes",
+    "timeout": 30,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": 1001@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": 1002@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"q\": \"is:open owner:None age:30d \"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-abandon",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"1001\", \"input\": {\"message\": \"Abandoning orphaned roll change.\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "abandon old changes.pigweed.abandon 1001",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@Expecting value: line 1 column 1 (char 0)@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"1001\",@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"Abandoning orphaned roll change.\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/1001@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-abandon",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"1002\", \"input\": {\"message\": \"Abandoning orphaned roll change.\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "abandon old changes.pigweed.abandon 1002",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@Expecting value: line 1 column 1 (char 0)@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"1002\",@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"Abandoning orphaned roll change.\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/1002@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "abandon old changes.pigweed.success",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "not ready",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@ready_to_upload@false@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed",
+    "~followup_annotations": [
+      "@@@STEP_LINK@applied pigweed:1234 (.)@https://pigweed-review.googlesource.com/c/1234@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.options",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\ninitialize_submodules: true\nmatch_branch: true\nuse_trigger: true\n@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.options with defaults",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\ninitialize_submodules: true\nrepo_init_timeout_sec: 120\nrepo_sync_timeout_sec: 900\nnumber_of_attempts: 3\nmatch_branch: true\nsubmodule_timeout_sec: 600\nuse_trigger: true\nmax_attempts: 2\n@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.process gitiles commit",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-query",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"params\": {\"q\": \"commit:h3ll0\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.change data.process gitiles commit.number",
+    "timeout": 30,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": \"1234\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"branch\": \"main\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"project\": \"pigweed\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"q\": \"commit:h3ll0\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.changes",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.changes.pigweed:1234",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_SUMMARY_TEXT@Change(number=1234, remote='https://pigweed.googlesource.com/pigweed/pigweed', ref='h3ll0', rebase=False, project='pigweed', branch='releases/google/test_20260501', gerrit_name='pigweed', submitted=True, patchset=None, path=None, base=None, base_type=None, is_merge=False, commit_message='', topic=None, current_revision='h3ll0', primary=True, submit_requirements={}, uploader=None)@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.branch names",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@['releases/google/test_20260501']@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.superproject has branch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "ls-remote",
+      "--heads",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "releases/google/test_20260501"
+    ],
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.superproject has branch.git ls-remote releases/google/test_20260501",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@stdout@@@@",
+      "@@@STEP_LOG_END@stdout@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.superproject has branch.head",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_SUMMARY_TEXT@''@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.no branch names match",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.cache",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@miss@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[CACHE]/git"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.ensure git cache dir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "",
+      "[CACHE]/git/.GUARD_FILE"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.write git cache guard file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.makedirs",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.cache.miss",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@SET_BUILD_PROPERTY@checkout https://pigweed.googlesource.com/pigweed/pigweed@\"cache miss\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "init"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git init",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "remote.origin.url",
+      "https://pigweed.googlesource.com/pigweed/pigweed"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.remote set-url",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "fetch.uriprotocols",
+      "https"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.enable packfiles",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.cache.timeout 10s",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--prune",
+      "--tags",
+      "--jobs",
+      "4",
+      "origin",
+      "--recurse-submodules"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git fetch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "merge",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git merge",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync",
+      "--recursive"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git submodule sync",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.cache.timeout 10s (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive",
+      "--force",
+      "--jobs",
+      "4"
+    ],
+    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.git submodule update",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "remove",
+      "[CACHE]/git/.GUARD_FILE"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.cache.remove git cache guard file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copytree",
+      "--symlinks",
+      "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
+      "[START_DIR]/co"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.copy from cache",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git checkout.timeout 10s",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/co"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "deadline": {
+        "grace_period": 30.0,
+        "soft_deadline": 1337000019.0
+      },
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.makedirs",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "init"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git init",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "remote",
+      "add",
+      "origin",
+      "https://pigweed.googlesource.com/pigweed/pigweed"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git remote",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "core.longpaths",
+      "true"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.set core.longpaths",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "fetch.uriprotocols",
+      "https"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.enable packfiles",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--tags",
+      "--jobs",
+      "4",
+      "origin",
+      "main",
+      "--recurse-submodules"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git fetch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git checkout",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git rev-parse",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.git clean",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git checkout.submodule",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git checkout.submodule.submodule",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.submodule.submodule.git submodule sync",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive",
+      "--force",
+      "--jobs",
+      "4"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git checkout.submodule.submodule.git submodule update",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@4@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git rev-parse",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.apply pigweed:1234",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LINK@gerrit@https://pigweed-review.googlesource.com/c/1234@@@",
+      "@@@STEP_LINK@gitiles@https://pigweed.googlesource.com/pigweed/pigweed/+/h3ll0@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.apply pigweed:1234.timeout 10s",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--jobs",
+      "4",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "h3ll0",
+      "--no-recurse-submodules"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.git fetch patch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "--force",
+      "-b",
+      "working",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.git checkout patch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "remote",
+      "add",
+      "https___pigweed_googlesource_com_pigweed_pigweed",
+      "https://pigweed.googlesource.com/pigweed/pigweed"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.git remote add",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.apply pigweed:1234.timeout 10s (2)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--jobs",
+      "4",
+      "https___pigweed_googlesource_com_pigweed_pigweed",
+      "refs/heads/releases/google/test_20260501"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.git fetch branch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "branch",
+      "--set-upstream-to=https___pigweed_googlesource_com_pigweed_pigweed/releases/google/test_20260501"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.git set upstream",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.git rev-parse",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--oneline",
+      "-n",
+      "10"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.post-rebase log",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.apply pigweed:1234.timeout 10s (3)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "--detach"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.detach",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.git submodule sync",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive",
+      "--jobs",
+      "4"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.git submodule update",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.apply pigweed:1234.reattach",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "RECIPE_MODULE[pigweed::checkout]/resources/submodule_status.py",
+      "[START_DIR]/co",
+      "/path/to/tmp/json",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/co",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.submodule status",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.matching pigweed:1234",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@no matching submodules@@@",
+      "@@@STEP_LINK@gerrit@https://pigweed-review.googlesource.com/c/1234@@@",
+      "@@@STEP_LINK@gitiles@https://pigweed.googlesource.com/pigweed/pigweed/+/h3ll0@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[{\"applied\": true, \"base\": \"HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_\", \"base_type\": \"submitted_commit_hash\", \"branch\": \"releases/google/test_20260501\", \"commit_message\": \"\", \"current_revision\": \"h3ll0\", \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"primary\": true, \"project\": \"pigweed\", \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null, \"uploader\": null}]",
+      "[CLEANUP]/tmp_tmp_1"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.write changes.json",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@tmp_tmp_1@[{\"applied\": true, \"base\": \"HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_\", \"base_type\": \"submitted_commit_hash\", \"branch\": \"releases/google/test_20260501\", \"commit_message\": \"\", \"current_revision\": \"h3ll0\", \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"primary\": true, \"project\": \"pigweed\", \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null, \"uploader\": null}]@@@",
+      "@@@STEP_LOG_END@tmp_tmp_1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git log",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--oneline",
+      "-n",
+      "10"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git log.[START_DIR]/co",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.base",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_\"@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision_type@\"submitted_commit_hash\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-f",
+      "-d"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git clean",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "status"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git status",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.status",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@applied [Change(number=1234, remote='https://pigweed.googlesource.com/pigweed/pigweed', ref='h3ll0', rebase=False, project='pigweed', branch='releases/google/test_20260501', gerrit_name='pigweed', submitted=True, patchset=None, path='.', base='HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_HEAD_', base_type='submitted_commit_hash', is_merge=False, commit_message='', topic=None, current_revision='h3ll0', primary=True, submit_requirements={}, uploader=None)]\nnot applied []@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0o777",
+      "[START_DIR]/snapshot"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.mkdir",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "status",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.submodule-status",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "submodule status filler text",
+      "[START_DIR]/snapshot/submodules.log"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.write submodule snapshot",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@submodules.log@submodule status filler text@@@",
+      "@@@STEP_LOG_END@submodules.log@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--oneline",
+      "-n",
+      "10"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.log",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "",
+      "[START_DIR]/snapshot/git.log"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.write git log",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_END@git.log@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd"
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/co/pw_env_setup/py/pw_env_setup/cipd_setup/pigweed.json",
+      "/path/to/tmp/"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.read pigweed.json",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@{@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@  \"packages\": [@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@    {@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      \"_comment\": \"comments should be preserved\",@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      \"path\": \"cipd/${platform}\",@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      \"platforms\": [@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@        \"linux-amd64\",@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@        \"windows-amd64\"@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      ],@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@        \"git_revision:123\"@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      ]@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@    }@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@  ]@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@}@@@",
+      "@@@STEP_LOG_END@pigweed.json@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "describe",
+      "cipd/linux-amd64",
+      "-version",
+      "latest",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.cipd describe cipd/linux-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pin\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"cipd/linux-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"refs\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"ref\": \"latest\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"buildbot_build:some.waterfall/builder/1234\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_repository:https://chromium.googlesource.com/some/repo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "describe",
+      "cipd/windows-amd64",
+      "-version",
+      "latest",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.cipd describe cipd/windows-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pin\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"cipd/windows-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"refs\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"ref\": \"latest\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"buildbot_build:some.waterfall/builder/1234\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_repository:https://chromium.googlesource.com/some/repo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.common tags",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@git_revision:397a2597cdc237f3026e6143b683be4b9ab60540@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.check number of instances",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "describe",
+      "cipd/linux-amd64",
+      "-version",
+      "git_revision:397a2597cdc237f3026e6143b683be4b9ab60540",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.check number of instances.cipd describe cipd/linux-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pin\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-git_revision:397\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"cipd/linux-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"refs\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"ref\": \"latest\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"buildbot_build:some.waterfall/builder/1234\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_repository:https://chromium.googlesource.com/some/repo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "describe",
+      "cipd/windows-amd64",
+      "-version",
+      "git_revision:397a2597cdc237f3026e6143b683be4b9ab60540",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.check number of instances.cipd describe cipd/windows-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pin\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-git_revision:397\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"cipd/windows-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"refs\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"ref\": \"latest\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"buildbot_build:some.waterfall/builder/1234\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_repository:https://chromium.googlesource.com/some/repo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.new version",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@git_revision:397a2597cdc237f3026e6143b683be4b9ab60540@@@",
+      "@@@STEP_LOG_LINE@old version@git_revision:123@@@",
+      "@@@STEP_LOG_END@old version@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.checking against old version",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.checking against old version.cipd/linux-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "describe",
+      "cipd/linux-amd64",
+      "-version",
+      "git_revision:123",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.checking against old version.cipd/linux-amd64.cipd describe cipd/linux-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pin\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-git_revision:123\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"cipd/linux-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"refs\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"ref\": \"latest\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"buildbot_build:some.waterfall/builder/1234\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_repository:https://chromium.googlesource.com/some/repo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.checking against old version.cipd/windows-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "describe",
+      "cipd/windows-amd64",
+      "-version",
+      "git_revision:123",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.checking against old version.cipd/windows-amd64.cipd describe cipd/windows-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pin\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-git_revision:123\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"cipd/windows-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"refs\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"ref\": \"latest\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"buildbot_build:some.waterfall/builder/1234\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_repository:https://chromium.googlesource.com/some/repo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.check package uploader",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.check package uploader.cipd/linux-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "describe",
+      "cipd/linux-amd64",
+      "-version",
+      "git_revision:397a2597cdc237f3026e6143b683be4b9ab60540",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.check package uploader.cipd/linux-amd64.cipd describe cipd/linux-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pin\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-git_revision:397\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"cipd/linux-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"refs\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"ref\": \"latest\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"buildbot_build:some.waterfall/builder/1234\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_repository:https://chromium.googlesource.com/some/repo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.check package uploader.cipd/linux-amd64.allowlist empty",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.check package uploader.cipd/windows-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "describe",
+      "cipd/windows-amd64",
+      "-version",
+      "git_revision:397a2597cdc237f3026e6143b683be4b9ab60540",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.check package uploader.cipd/windows-amd64.cipd describe cipd/windows-amd64",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"pin\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"instance_id\": \"resolved-instance_id-of-git_revision:397\",@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"package\": \"cipd/windows-amd64\"@@@",
+      "@@@STEP_LOG_LINE@json.output@    },@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"refs\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"modified_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"ref\": \"latest\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ],@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"buildbot_build:some.waterfall/builder/1234\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_repository:https://chromium.googlesource.com/some/repo\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_by\": \"user:44-blablbla@developer.gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"registered_ts\": 1446574210,@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"tag\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "cipd.check package uploader.cipd/windows-amd64.allowlist empty",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "{\n  \"packages\": [\n    {\n      \"_comment\": \"comments should be preserved\",\n      \"path\": \"cipd/${platform}\",\n      \"platforms\": [\n        \"linux-amd64\",\n        \"windows-amd64\"\n      ],\n      \"tags\": [\n        \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"\n      ]\n    }\n  ]\n}\n",
+      "[START_DIR]/co/pw_env_setup/py/pw_env_setup/cipd_setup/pigweed.json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "cipd.write pigweed.json",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@{@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@  \"packages\": [@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@    {@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      \"_comment\": \"comments should be preserved\",@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      \"path\": \"cipd/${platform}\",@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      \"platforms\": [@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@        \"linux-amd64\",@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@        \"windows-amd64\"@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      ],@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@        \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\"@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@      ]@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@    }@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@  ]@@@",
+      "@@@STEP_LOG_LINE@pigweed.json@}@@@",
+      "@@@STEP_LOG_END@pigweed.json@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "commit message",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@commit message@roll: cipd@@@",
+      "@@@STEP_LOG_LINE@commit message@@@@",
+      "@@@STEP_LOG_LINE@commit message@From git_revision:123@@@",
+      "@@@STEP_LOG_LINE@commit message@To git_revision:397a2597cdc237f3026e6143b683be4b9ab60540@@@",
+      "@@@STEP_LOG_LINE@commit message@@@@",
+      "@@@STEP_LOG_LINE@commit message@Roll-Count: 1@@@",
+      "@@@STEP_LOG_END@commit message@@@",
+      "@@@SET_BUILD_PROPERTY@_roll_count@1@@@",
+      "@@@SET_BUILD_PROPERTY@_roll_names@[\"cipd\"]@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "commit message.cipd",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@{'name': 'cipd', 'spec': 'cipd/${platform}', 'old': 'git_revision:123', 'new': 'git_revision:397a2597cdc237f3026e6143b683be4b9ab60540'}@@@",
+      "@@@SET_BUILD_PROPERTY@cipd@{\"name\": \"cipd\", \"new\": \"git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\", \"old\": \"git_revision:123\", \"spec\": \"cipd/${platform}\"}@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "ready_to_upload",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@ready_to_upload@true@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "ls-files",
+      "--modified",
+      "--deleted",
+      "--exclude-standard"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "check for no-op commit: unstaged / ignored files",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@stdout@hello@@@",
+      "@@@STEP_LOG_END@stdout@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "diff",
+      "HEAD",
+      "--cached",
+      "--name-only"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "check for no-op commit: staged files",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@stdout@staged-foo.txt@@@",
+      "@@@STEP_LOG_LINE@stdout@staged-bar.txt@@@",
+      "@@@STEP_LOG_END@stdout@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "add",
+      "--update"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "git add",
+    "timeout": 300.0
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "git rev-parse",
+    "timeout": 300.0
+  },
+  {
+    "cmd": [],
+    "name": "calculate Change-Id",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "diff",
+      "--unified=0",
+      "--cached"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "calculate Change-Id.git diff",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@diff --git a/foo.txt b/foo.txt@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@--- a/foo.txt@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@+++ b/foo.txt@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@@@ -16 +16 @@@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@-        foo = 5@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@+        foo = 6@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@diff --git a/bar.txt b/bar.txt@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@--- a/bar.txt@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@+++ b/bar.txt@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@@@ -5 +5 @@@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@-        bar = 0@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@+        bar = 1@@@",
+      "@@@STEP_LOG_LINE@diff (without hashes)@@@@",
+      "@@@STEP_LOG_END@diff (without hashes)@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "hash-object",
+      "diff --git a/foo.txt b/foo.txt\n--- a/foo.txt\n+++ b/foo.txt\n@@ -16 +16 @@\n-        foo = 5\n+        foo = 6\ndiff --git a/bar.txt b/bar.txt\n--- a/bar.txt\n+++ b/bar.txt\n@@ -5 +5 @@\n-        bar = 0\n+        bar = 1\n####builder########"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "calculate Change-Id.git hash-object",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-query",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"params\": {\"o\": [\"MESSAGES\", \"DETAILED_ACCOUNTS\"], \"q\": \"change:pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]/co",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "check for identical roll",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@[]@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"o\": [@@@",
+      "@@@STEP_LOG_LINE@json.input@      \"MESSAGES\",@@@",
+      "@@@STEP_LOG_LINE@json.input@      \"DETAILED_ACCOUNTS\"@@@",
+      "@@@STEP_LOG_LINE@json.input@    ],@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"q\": \"change:pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "commit",
+      "-m",
+      "roll: cipd\n\nFrom git_revision:123\nTo git_revision:397a2597cdc237f3026e6143b683be4b9ab60540\n\nRoll-Count: 1\nRoller-URL: https://cr-buildbucket.appspot.com/build/8945511751514863184\nCQ-Do-Not-Cancel-Tryjobs: true\nChange-Id: I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c",
+      "-a",
+      "--no-verify"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "git commit",
+    "timeout": 1200.0
+  },
+  {
+    "cmd": [
+      "git",
+      "push",
+      "--push-option",
+      "nokeycheck",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "HEAD:refs/for/main%l=Commit-Queue+1,hashtag=roll%2dcipd"
+    ],
+    "cwd": "[START_DIR]/co",
+    "env": {
+      "GIT_ADVICE": "0",
+      "GIT_CONFIG_COUNT": "3",
+      "GIT_CONFIG_KEY_0": "gc.auto",
+      "GIT_CONFIG_KEY_1": "gc.autoDetach",
+      "GIT_CONFIG_KEY_2": "gc.autoPackLimit",
+      "GIT_CONFIG_VALUE_0": "0",
+      "GIT_CONFIG_VALUE_1": "0",
+      "GIT_CONFIG_VALUE_2": "0"
+    },
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "git push",
+    "timeout": 180.0,
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@stdout@@@@",
+      "@@@STEP_LOG_END@stdout@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "output gerrit change id",
+    "~followup_annotations": [
+      "@@@SET_BUILD_PROPERTY@gerrit_changes@[{\"change_id\": \"pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c\", \"dry_run\": true, \"host\": \"pigweed-review.googlesource.com\"}]@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "check for completion"
+  },
+  {
+    "cmd": [],
+    "name": "check for completion.check if done (0)",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c\", \"params\": {\"o\": [\"CURRENT_REVISION\", \"DETAILED_ACCOUNTS\", \"SUBMITTABLE\"]}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "check for completion.check if done (0).status",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"_number\": 123456,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"current_revision\": \"abc123\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"current_revision_number\": 3,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"labels\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"Commit-Queue\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"all\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@        {@@@",
+      "@@@STEP_LOG_LINE@json.output@          \"value\": 2@@@",
+      "@@@STEP_LOG_LINE@json.output@        }@@@",
+      "@@@STEP_LOG_LINE@json.output@      ],@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"approved\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"email\": \"roller@fuchsia-service-account.com\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"status\": \"MERGED\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c\",@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"params\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"o\": [@@@",
+      "@@@STEP_LOG_LINE@json.input@      \"CURRENT_REVISION\",@@@",
+      "@@@STEP_LOG_LINE@json.input@      \"DETAILED_ACCOUNTS\",@@@",
+      "@@@STEP_LOG_LINE@json.input@      \"SUBMITTABLE\"@@@",
+      "@@@STEP_LOG_LINE@json.input@    ]@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[START_DIR]/cipd_tool/infra/tools/luci/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07/gerrit",
+      "change-abandon",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c\", \"input\": {\"message\": \"Dry run passed.\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "abandon roll",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_TEXT@dry run passed@@@",
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@Expecting value: line 1 column 1 (char 0)@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c\",@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"Dry run passed.\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c@@@"
+    ]
+  },
+  {
+    "name": "$result",
+    "summaryMarkdown": "Dry run succeeded. [gerrit link](https://pigweed-review.googlesource.com/q/pigweed/pigweed~main~I28135185e8ac8c69d0b894c4be54ddbb0ea69d5c)"
+  }
+]
\ No newline at end of file
diff --git a/recipes/roller.proto b/recipes/roller.proto
index 1a735f6..bd05427 100644
--- a/recipes/roller.proto
+++ b/recipes/roller.proto
@@ -111,13 +111,4 @@
   // Id of the multi_roller build that launched this builder, if applicable.
   // String because large ints don't always get preserved through JSON.
   string parent_build_id = 23;
-
-  // Whether to match the destination branch to the triggering branch. This is
-  // useful for rollers of release branches where the downstream change is on
-  // 'refs/heads/releases/$RELEASE', the destination repo has that branch, but
-  // we don't want to configure a separate roller for each release branch.
-  // If this is set then auto_roller_options.upstream_ref,
-  // override_auto_roller_options.upstream_ref, and checkout_options.branch will
-  // be overwritten.
-  string match_triggering_branch = 24;
 }
diff --git a/recipes/roller.py b/recipes/roller.py
index bb24a6d..af3582b 100644
--- a/recipes/roller.py
+++ b/recipes/roller.py
@@ -33,7 +33,6 @@
 
 import contextlib
 import itertools
-import re
 from collections.abc import Iterator, Sequence
 from typing import Any
 
@@ -43,6 +42,9 @@
 
 from PB.go.chromium.org.luci.buildbucket.proto import build as build_pb
 from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb
+from PB.go.chromium.org.luci.scheduler.api.scheduler.v1 import (
+    triggers as triggers_pb,
+)
 from PB.recipe_engine import result as result_pb
 from PB.recipe_modules.fuchsia.auto_roller.options import (
     Options as AutoRollerOptions,
@@ -89,8 +91,8 @@
     'pigweed/variable_roll',
     'recipe_engine/buildbucket',
     'recipe_engine/file',
-    'recipe_engine/json',
     'recipe_engine/properties',
+    'recipe_engine/scheduler',
     'recipe_engine/step',
     'recipe_engine/time',
 ]
@@ -120,27 +122,9 @@
     not_ready = api.step.empty('not ready').presentation
     not_ready.properties['ready_to_upload'] = False
 
-    if props.match_triggering_branch:
-        triggering_changes = api.checkout.triggering_changes(
-            props.checkout_options
-        )
-        if len(triggering_changes) != 1:
-            api.step.empty(
-                f'len(triggering_changes) {len(triggering_changes)} != 1',
-                status='FAILURE',
-            )
-
-        change = triggering_changes[0]
-        if not change.submitted:
-            api.step.empty(
-                f'triggering change {change} is not submitted',
-                status='FAILURE',
-            )
-
-        props.checkout_options.branch = change.branch
-        props.auto_roller_options.upstream_ref = change.branch
-        props.override_auto_roller_options.upstream_ref = change.branch
-
+    if api.properties.get('release_branch_prefix'):
+        props.checkout_options.use_trigger = True
+        props.checkout_options.match_branch = True
     checkout = api.checkout(props.checkout_options)
 
     env: api.checkout.Environment | None = None
@@ -512,101 +496,6 @@
     )
 
     yield api.test(
-        'match_triggering_branch_success',
-        api.buildbucket.ci_build(
-            git_repo=api.checkout.pigweed_repo,
-            git_ref='refs/heads/release-1.0',
-        ),
-        api.override_step_data(
-            'change data.process gitiles commit.number',
-            api.json.output(
-                [
-                    {
-                        '_number': '1234',
-                        'branch': 'release-1.0',
-                        'project': 'pigweed/pigweed',
-                    }
-                ]
-            ),
-        ),
-        properties(
-            cipd_packages=api.cipd_roll.package_props('cipd/${platform}'),
-            match_triggering_branch='true',
-        ),
-        api.cipd_roll.read_file_step_data(
-            'cipd',
-            'pigweed.json',
-            api.cipd_roll.package(
-                'cipd/${platform}',
-                'git_revision:123',
-                platforms=['linux-amd64', 'windows-amd64'],
-            ),
-        ),
-        expected_roll(
-            'cipd',
-            old='git_revision:123',
-            new='git_revision:397a2597cdc237f3026e6143b683be4b9ab60540',
-        ),
-        api.auto_roller.dry_run_success(),
-        api.post_check(post_process.PropertyEquals, '_roll_count', 1),
-        api.post_check(
-            post_process.StepCommandContains,
-            'git push',
-            [re.compile(r'refs/for/release-1.0')],
-        ),
-        api.post_process(post_process.DropExpectation),
-    )
-
-    yield api.test(
-        'match_triggering_branch_not_submitted',
-        api.checkout.try_test_data(),
-        properties(
-            cipd_packages=api.cipd_roll.package_props('cipd/${platform}'),
-            match_triggering_branch='true',
-        ),
-        api.post_check(
-            post_process.MustRunRE,
-            r'.*triggering change .* is not submitted',
-        ),
-        api.post_process(post_process.DropExpectation),
-        status='FAILURE',
-    )
-
-    yield api.test(
-        'match_triggering_branch_multiple_changes',
-        api.checkout.try_test_data(
-            gerrit_changes=[
-                api.checkout.cl('host1', 'proj1', 123),
-                api.checkout.cl('host2', 'proj2', 456),
-            ]
-        ),
-        properties(
-            cipd_packages=api.cipd_roll.package_props('cipd/${platform}'),
-            match_triggering_branch='true',
-        ),
-        api.post_check(
-            post_process.MustRun,
-            'len(triggering_changes) 2 != 1',
-        ),
-        api.post_process(post_process.DropExpectation),
-        status='FAILURE',
-    )
-
-    yield api.test(
-        'match_triggering_branch_no_changes',
-        properties(
-            cipd_packages=api.cipd_roll.package_props('cipd/${platform}'),
-            match_triggering_branch='true',
-        ),
-        api.post_check(
-            post_process.MustRun,
-            'len(triggering_changes) 0 != 1',
-        ),
-        api.post_process(post_process.DropExpectation),
-        status='FAILURE',
-    )
-
-    yield api.test(
         'mega',
         api.buildbucket.ci_build(
             tags=[common_pb.StringPair(key='user_agent', value='bisector')],
@@ -702,3 +591,36 @@
         api.post_check(post_process.PropertyEquals, '_roll_count', 10),
         # api.post_process(post_process.DropExpectation),
     )
+
+    yield api.test(
+        'release_branch_trigger',
+        api.buildbucket.ci_build(
+            git_repo='https://pigweed.googlesource.com/pigweed/pigweed',
+            revision='h3ll0',
+        ),
+        api.scheduler(
+            triggers=[
+                triggers_pb.Trigger(
+                    gitiles=triggers_pb.GitilesTrigger(
+                        repo='https://pigweed.googlesource.com/pigweed/pigweed',
+                        revision='h3ll0',
+                        ref='refs/heads/releases/google/test_20260501',
+                    ),
+                ),
+            ],
+        ),
+        api.properties(release_branch_prefix='releases/'),
+        properties(
+            cipd_packages=api.cipd_roll.package_props('cipd/${platform}'),
+        ),
+        api.cipd_roll.read_file_step_data(
+            'cipd',
+            'pigweed.json',
+            api.cipd_roll.package(
+                'cipd/${platform}',
+                'git_revision:123',
+                platforms=['linux-amd64', 'windows-amd64'],
+            ),
+        ),
+        api.auto_roller.success(),
+    )
diff --git a/recipes/workflows.proto b/recipes/workflows.proto
index 0a30830..93d70c2 100644
--- a/recipes/workflows.proto
+++ b/recipes/workflows.proto
@@ -41,4 +41,8 @@
   // Version of Bazel to use. If empty, bazelisk uses the .bazelversion file.
   // Cannot be set if the pigweed.latest_bazel experiment is active.
   string bazel_version = 7;
+
+  // Prefix for release branches. If set, builds on release branches will be
+  // skipped if the branch doesn't exist in the downstream repo.
+  string release_branch_prefix = 8;
 }
diff --git a/recipes/workflows.py b/recipes/workflows.py
index e0c3154..15abf7a 100644
--- a/recipes/workflows.py
+++ b/recipes/workflows.py
@@ -28,6 +28,7 @@
     recipe_test_api,
 )
 
+from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb
 from PB.pigweed.pw_build.proto import build_driver as build_driver_pb
 from PB.pigweed.pw_build.proto import workflows as workflows_pb
 from PB.recipe_engine import result as result_pb
@@ -101,7 +102,28 @@
     if res := api.ci_status.exit_early_in_recipe_testing_if_failing():
         return res  # pragma: no cover
 
-    checkout = api.checkout(props.checkout_options)
+    is_release_branch = False
+    if props.release_branch_prefix and props.checkout_options.branch:
+        if props.checkout_options.branch.startswith(
+            props.release_branch_prefix
+        ):
+            is_release_branch = True
+
+    try:
+        checkout = api.checkout(props.checkout_options)
+    except api.step.StepFailure as e:
+        if is_release_branch:
+            step_text = f'Branch {props.checkout_options.branch} not found in downstream repo or checkout failed'
+            pres = api.step.empty(
+                'skip missing release branch',
+                step_text=step_text,
+            ).presentation
+            pres.properties['early_exit_reason'] = 'skip missing release branch'
+            return result_pb.RawResult(
+                summary_markdown=step_text,
+                status=common_pb.SUCCESS,
+            )
+        raise e
 
     if res := api.roll_tryjob_reuse.exit_early_if_applicable(checkout.changes):
         return res  # pragma: no cover
@@ -425,9 +447,17 @@
 def GenTests(
     api: recipe_test_api.RecipeTestApi,
 ) -> Iterator[recipe_test_api.TestData]:
-    def properties(**kwargs: Any) -> recipe_test_api.TestData:
+    def properties(
+        checkout_overrides=None, **kwargs: Any
+    ) -> recipe_test_api.TestData:
         props = InputProperties(**kwargs)
-        props.checkout_options.CopyFrom(api.checkout.git_options())
+        if not props.HasField('checkout_options'):
+            props.checkout_options.CopyFrom(
+                api.checkout.git_options(**(checkout_overrides or {}))
+            )
+        elif checkout_overrides:
+            for k, v in checkout_overrides.items():
+                setattr(props.checkout_options, k, v)
         return api.properties(props)
 
     def must_upload(
@@ -490,3 +520,51 @@
         ),
         api.post_process(post_process.DropExpectation),
     )
+
+    yield api.test(
+        'release_branch_success',
+        properties(
+            release_branch_prefix='releases/',
+            checkout_overrides=dict(
+                branch='releases/foo',
+            ),
+        ),
+        api.buildbucket.try_build(),
+        api.checkout.try_test_data(),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
+        'release_branch_missing',
+        properties(
+            release_branch_prefix='releases/',
+            checkout_overrides=dict(
+                branch='releases/foo',
+            ),
+        ),
+        api.buildbucket.try_build(),
+        api.checkout.try_test_data(),
+        api.step_data('checkout pigweed.git checkout.git checkout', retcode=1),
+        api.post_process(post_process.DropExpectation),
+    )
+
+    yield api.test(
+        'checkout_failure',
+        properties(),
+        api.buildbucket.try_build(),
+        api.checkout.try_test_data(),
+        api.step_data('checkout pigweed.git checkout.git checkout', retcode=1),
+        api.post_process(post_process.DropExpectation),
+        status='INFRA_FAILURE',
+    )
+
+    yield api.test(
+        'checkout_overrides_with_options',
+        properties(
+            checkout_options=api.checkout.git_options(branch='main'),
+            checkout_overrides=dict(branch='overridden'),
+        ),
+        api.buildbucket.try_build(),
+        api.checkout.try_test_data(),
+        api.post_process(post_process.DropExpectation),
+    )