bazel_roller: Move Bazel-specific parts to module

Move the Bazel-specific parts of the bazel_roller recipe to a new
module, bazel_roll. Not putting this in the pre-existing bazel module
since we shouldn't have to test all Bazel-based builders when modifying
Bazel roller logic.

Leaving api.bazel.update_commit_hash() in the bazel module because it's
tightly coupled with api.path.retrieve_git_repository_attributes() which
is also used by the checkout module.

Bug: b/341756093
Change-Id: Ibdc1cc85e2d0dcee24e26d5137f8fcc0d107a44b
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/230293
Reviewed-by: Danielle Kay <danikay@google.com>
Commit-Queue: Danielle Kay <danikay@google.com>
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/recipe_modules/bazel_roll/__init__.py b/recipe_modules/bazel_roll/__init__.py
new file mode 100644
index 0000000..2ae95e2
--- /dev/null
+++ b/recipe_modules/bazel_roll/__init__.py
@@ -0,0 +1,26 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+# pylint: disable=missing-docstring
+
+from __future__ import annotations
+
+DEPS = [
+    'pigweed/bazel',
+    'pigweed/checkout',
+    'pigweed/roll_util',
+    'recipe_engine/buildbucket',
+    'recipe_engine/path',
+    'recipe_engine/step',
+]
diff --git a/recipe_modules/bazel_roll/api.py b/recipe_modules/bazel_roll/api.py
new file mode 100644
index 0000000..bbce5ef
--- /dev/null
+++ b/recipe_modules/bazel_roll/api.py
@@ -0,0 +1,210 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from __future__ import annotations
+
+import configparser
+import dataclasses
+import io
+import re
+from typing import TYPE_CHECKING
+
+from PB.recipe_modules.pigweed.checkout.options import (
+    Options as CheckoutOptions,
+)
+from recipe_engine import recipe_api
+
+if TYPE_CHECKING:  # pragma: no cover
+    from typing import Generator
+    from PB.recipe_modules.pigweed.bazel_roll.git_repository import (
+        GitRepository,
+    )
+    from recipe_engine import config_types
+    from RECIPE_MODULES.pigweed.checkout import api as checkout_api
+    from RECIPE_MODULES.pigweed.roll_util import api as roll_util_api
+
+
+@dataclasses.dataclass
+class RevisionChange:
+    old: str
+    new: str
+
+
+class BazelRollApi(recipe_api.RecipeApi):
+
+    RevisionChange = RevisionChange
+
+    def workspace_path(
+        self,
+        root: config_types.Path,
+        value: str,
+    ) -> config_types.Path:
+        """Figure out the location of the WORKSPACE or MODULE.bazel file.
+
+        If value is '', look for root / 'WORKSPACE' and root / 'MODULE.bazel'.
+        If exactly one exists, return it. If not, error out.
+
+        If root / value is a file, return it.
+
+        If root / value is a directory, set root = root / value and apply the
+        above logic. This enables applying this logic to subdirectories.
+
+        Args:
+            api: Recipe API object.
+            root: Checkout root.
+            value: Relative path specified in properties.
+
+        Returns:
+            Path to the WORKSPACE or MODULE.bazel file.
+        """
+        if value:
+            value_path = root / value
+            self.m.path.mock_add_file(value_path)
+            if self.m.path.isfile(value_path):
+                return value_path
+            elif self.m.path.isdir(value_path):  # pragma: no cover
+                root = value_path
+            else:
+                self.m.step.empty(  # pragma: no cover
+                    f'{value_path} does not exist',
+                    status='FAILURE',
+                )
+
+        workspace = root / 'WORKSPACE'
+        module_bazel = root / 'MODULE.bazel'
+
+        self.m.path.mock_add_file(workspace)
+
+        if self.m.path.isfile(module_bazel) and self.m.path.isfile(workspace):
+            self.m.step.empty(  # pragma: no cover
+                f'{module_bazel} and {workspace} both exist',
+                status='FAILURE',
+            )
+
+        if self.m.path.isfile(module_bazel):
+            return module_bazel  # pragma: no cover
+
+        if self.m.path.isfile(workspace):
+            return workspace
+
+        self.m.step.empty(  # pragma: no cover
+            'no WORKSPACE or MODULE.bazel file found',
+            status='FAILURE',
+        )
+
+    def update_git_repository(
+        self,
+        checkout: checkout_self.m.CheckoutContext,
+        git_repository: GitRepository,
+    ) -> dict[str, self.m.roll_util.Roll]:
+        workspace_path = self.workspace_path(
+            checkout.root,
+            git_repository.workspace_path,
+        )
+        git_repository.branch = git_repository.branch or 'main'
+
+        new_revision: Optional[str] = None
+
+        # First, try to get new_revision from the trigger.
+        bb_remote: Optional[str] = None
+        commit: common_pb2.GitilesCommit = (
+            self.m.buildbucket.build.input.gitiles_commit
+        )
+        if commit and commit.project:
+            new_revision = commit.id
+            host: str = commit.host
+            bb_remote: str = f'https://{host}/{commit.project}'
+
+        # If we still don't have a revision then it wasn't in the trigger.
+        # (Perhaps this was manually triggered.) In this case we update to the
+        # property-specified branch HEAD.
+        if new_revision is None:
+            new_revision = git_repository.branch
+
+        # If this was triggered by a gitiles poller, check that the triggering
+        # repository matches project_remote. Exception: allow a trigger to be
+        # for the top-level project instead.
+        use_trigger_for_project = True
+
+        if bb_remote:
+            if checkout.remotes_equivalent(checkout.options.remote, bb_remote):
+                use_trigger_for_project = False
+
+            elif not checkout.remotes_equivalent(
+                git_repository.remote,
+                bb_remote,
+            ):
+                self.m.step.empty(
+                    'triggering repository ({}) does not match project remote '
+                    '({})'.format(bb_remote, git_repository.remote),
+                    status='FAILURE',
+                )
+
+        project_dir = self.m.path.start_dir / 'project'
+
+        project_checkout = self.m.checkout(
+            CheckoutOptions(
+                remote=git_repository.remote,
+                branch=git_repository.branch,
+                use_trigger=use_trigger_for_project,
+            ),
+            root=project_dir,
+        )
+
+        # In case new_revision is a branch name we need to retrieve the hash it
+        # resolves to.
+        if not re.search(r'^[0-9a-f]{40}$', new_revision):
+            new_revision = self.m.checkout.get_revision(
+                project_dir, 'get new revision', test_data='2' * 40
+            )
+
+        update_result = self.m.bazel.update_commit_hash(
+            checkout=checkout,
+            project_remote=git_repository.remote,
+            new_revision=new_revision,
+            path=workspace_path,
+        )
+        if not update_result:
+            self.m.step.empty('failed to update commit hash', status='FAILURE')
+
+        direction = self.m.roll_util.get_roll_direction(
+            project_dir, update_result.old_revision, new_revision
+        )
+
+        # If the primary roll is not necessary or is backwards we can exit
+        # immediately.
+        if not self.m.roll_util.can_roll(direction):
+            self.m.roll_util.skip_roll_step(
+                git_repository.remote, update_result.old_revision, new_revision
+            )
+            return
+
+        project_name = git_repository.name or update_result.project_name
+        if not project_name:
+            self.m.step.empty(
+                f'could not find name line in {workspace_path}',
+                status='FAILURE',
+            )
+
+        rolls: dict[str, self.m.roll_util.Roll] = {
+            workspace_path: self.m.roll_util.create_roll(
+                project_name=project_name,
+                old_revision=update_result.old_revision,
+                new_revision=new_revision,
+                proj_dir=project_dir,
+                direction=direction,
+            ),
+        }
+
+        return rolls
diff --git a/recipe_modules/bazel_roll/git_repository.proto b/recipe_modules/bazel_roll/git_repository.proto
new file mode 100644
index 0000000..6bf3698
--- /dev/null
+++ b/recipe_modules/bazel_roll/git_repository.proto
@@ -0,0 +1,32 @@
+// Copyright 2024 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+syntax = "proto3";
+
+package recipe_modules.pigweed.bazel_roll;
+
+message GitRepository {
+  // The path of the WORKSPACE or MODULE.bazel file to update. Required.
+  string workspace_path = 1;
+
+  // The name to use in the commit message. Defaults to the basename of the
+  // remote.
+  string name = 2;
+
+  // The remote URL of the project to update. Required.
+  string remote = 3;
+
+  // Branch to track. Default: "main".
+  string branch = 4;
+}
diff --git a/recipe_modules/bazel_roll/test_api.py b/recipe_modules/bazel_roll/test_api.py
new file mode 100644
index 0000000..8ecc739
--- /dev/null
+++ b/recipe_modules/bazel_roll/test_api.py
@@ -0,0 +1,40 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Test API for bazel_roll."""
+
+from PB.recipe_modules.pigweed.bazel_roll.git_repository import GitRepository
+from recipe_engine import recipe_test_api
+
+
+class BazelRollTestApi(recipe_test_api.RecipeTestApi):
+    """Test API for bazel_roll."""
+
+    def _url(self, x: str = 'pigweed/pigweed'):
+        assert ':' not in x
+        return f'https://pigweed.googlesource.com/{x}'
+
+    def git_repo(
+        self,
+        *,
+        workspace_path='',
+        name=None,
+        remote=None,
+        branch='main',
+    ):
+        return GitRepository(
+            workspace_path=workspace_path,
+            name=name,
+            remote=remote or self._url('pigweed/pigweed'),
+            branch=branch,
+        )
diff --git a/recipes/bazel_roller.expected/backwards.json b/recipe_modules/bazel_roll/tests/full.expected/backwards.json
similarity index 70%
copy from recipes/bazel_roller.expected/backwards.json
copy to recipe_modules/bazel_roll/tests/full.expected/backwards.json
index ef4f845..e046581 100644
--- a/recipes/bazel_roller.expected/backwards.json
+++ b/recipe_modules/bazel_roll/tests/full.expected/backwards.json
@@ -1,14 +1,17 @@
 [
   {
     "cmd": [],
-    "name": "checkout pigweed"
+    "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\n@@@"
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nuse_trigger: true\n@@@"
     ]
   },
   {
@@ -16,7 +19,165 @@
     "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: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nmatch_branch: true\nsubmodule_timeout_sec: 600\n@@@"
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\nuse_trigger: true\n@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "ls-remote",
+      "--heads",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "main"
+    ],
+    "name": "checkout pigweed.change data.git ls-remote",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@stdout@h3ll0\trefs/heads/main@@@",
+      "@@@STEP_LOG_END@stdout@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.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,
+    "name": "checkout pigweed.change data.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": "checkout pigweed.change data.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,
+    "name": "checkout pigweed.change data.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,
+    "name": "checkout pigweed.change data.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\": \"commit:h3ll0\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "checkout pigweed.change data.number",
+    "timeout": 30,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@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=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)@@@"
     ]
   },
   {
@@ -146,7 +307,7 @@
       "--jobs",
       "4",
       "origin",
-      "--recurse-submodules"
+      "--no-recurse-submodules"
     ],
     "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
     "infra_step": true,
@@ -171,21 +332,6 @@
     ]
   },
   {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync",
-      "--recursive"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git submodule sync",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
     "cmd": [],
     "name": "checkout pigweed.cache.timeout 10s (2)",
     "~followup_annotations": [
@@ -197,7 +343,6 @@
       "git",
       "submodule",
       "update",
-      "--init",
       "--recursive",
       "--force",
       "--jobs",
@@ -237,7 +382,7 @@
       "copytree",
       "--symlinks",
       "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-      "[START_DIR]/co"
+      "[START_DIR]/project"
     ],
     "infra_step": true,
     "name": "checkout pigweed.copy from cache",
@@ -269,7 +414,7 @@
       "ensure-directory",
       "--mode",
       "0o777",
-      "[START_DIR]/co"
+      "[START_DIR]/project"
     ],
     "infra_step": true,
     "luci_context": {
@@ -288,7 +433,7 @@
       "git",
       "init"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git init",
     "timeout": 300.0,
@@ -304,7 +449,7 @@
       "origin",
       "https://pigweed.googlesource.com/pigweed/pigweed"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git remote",
     "timeout": 600.0,
@@ -319,7 +464,7 @@
       "core.longpaths",
       "true"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.set core.longpaths",
     "timeout": 300.0,
@@ -334,7 +479,7 @@
       "fetch.uriprotocols",
       "https"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.set fetch.uriprotocols",
     "timeout": 300.0,
@@ -350,10 +495,9 @@
       "--jobs",
       "4",
       "origin",
-      "main",
-      "--recurse-submodules"
+      "main"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git fetch",
     "timeout": 1200.0,
@@ -368,7 +512,7 @@
       "-f",
       "FETCH_HEAD"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git checkout",
     "timeout": 600.0,
@@ -382,7 +526,7 @@
       "rev-parse",
       "HEAD"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git rev-parse",
     "timeout": 300.0,
@@ -398,7 +542,7 @@
       "-d",
       "-x"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git clean",
     "timeout": 600.0,
@@ -407,8 +551,30 @@
     ]
   },
   {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/project",
+    "name": "checkout pigweed.git rev-parse",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [],
-    "name": "checkout pigweed.git checkout.submodule",
+    "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@@@"
     ]
@@ -416,15 +582,139 @@
   {
     "cmd": [
       "git",
-      "submodule",
-      "sync"
+      "fetch",
+      "--jobs",
+      "4",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "h3ll0",
+      "--no-recurse-submodules"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
-    "name": "checkout pigweed.git checkout.submodule.git submodule sync",
+    "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]/project",
+    "infra_step": true,
+    "name": "checkout pigweed.apply pigweed:1234.git checkout patch",
     "timeout": 600.0,
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "remote",
+      "add",
+      "https___pigweed_googlesource_com_pigweed_pigweed",
+      "https://pigweed.googlesource.com/pigweed/pigweed"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "name": "checkout pigweed.apply pigweed:1234.git remote add",
+    "timeout": 600.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/main"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "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/main"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "name": "checkout pigweed.apply pigweed:1234.git set upstream",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/project",
+    "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]/project",
+    "infra_step": true,
+    "name": "checkout pigweed.apply pigweed:1234.post-rebase log",
+    "timeout": 600.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]/project",
+    "name": "checkout pigweed.apply pigweed:1234.detach",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
     ]
   },
   {
@@ -434,16 +724,73 @@
       "update",
       "--init",
       "--recursive",
-      "--force",
       "--jobs",
       "4"
     ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.submodule.git submodule update",
+    "cwd": "[START_DIR]/project",
+    "name": "checkout pigweed.apply pigweed:1234.git submodule update",
     "timeout": 600,
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-"
+    ],
+    "cwd": "[START_DIR]/project",
+    "name": "checkout pigweed.apply pigweed:1234.reattach",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "RECIPE_MODULE[pigweed::checkout]/resources/submodule_status.py",
+      "[START_DIR]/project",
+      "/path/to/tmp/json",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/project",
+    "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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]",
+      "[CLEANUP]/tmp_tmp_1"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]@@@",
+      "@@@STEP_LOG_END@tmp_tmp_1@@@"
     ]
   },
   {
@@ -461,14 +808,23 @@
       "-n",
       "10"
     ],
-    "cwd": "[START_DIR]/co",
-    "name": "checkout pigweed.git log.[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
+    "name": "checkout pigweed.git log.[START_DIR]/project",
     "timeout": 600.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",
@@ -476,7 +832,7 @@
       "-f",
       "-d"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "name": "checkout pigweed.git clean",
     "timeout": 600.0,
     "~followup_annotations": [
@@ -488,7 +844,7 @@
       "git",
       "status"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "name": "checkout pigweed.git status",
     "timeout": 600.0,
     "~followup_annotations": [
@@ -496,6 +852,14 @@
     ]
   },
   {
+    "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=None, branch='main', 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=None)]\nnot applied []@@@"
+    ]
+  },
+  {
     "cmd": [
       "vpython3",
       "-u",
@@ -520,7 +884,7 @@
       "status",
       "--recursive"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "name": "checkout pigweed.submodule-status",
     "timeout": 600.0,
     "~followup_annotations": [
@@ -554,7 +918,7 @@
       "-n",
       "10"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "name": "checkout pigweed.log",
     "timeout": 600.0,
     "~followup_annotations": [
@@ -580,932 +944,6 @@
     ]
   },
   {
-    "cmd": [],
-    "name": "checkout pigweed (2)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@applied pigweed:1234 (.)@https://pigweed-review.googlesource.com/c/1234@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nuse_trigger: true\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\nuse_trigger: true\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "ls-remote",
-      "--heads",
-      "https://pigweed.googlesource.com/pigweed/pigweed",
-      "main"
-    ],
-    "name": "checkout pigweed (2).change data.git ls-remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@",
-      "@@@STEP_LOG_LINE@stdout@h3ll0\trefs/heads/main@@@",
-      "@@@STEP_LOG_END@stdout@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data.ensure infra/tools/luci/gerrit/${platform}",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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\": \"commit:h3ll0\"}}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).change data.number",
-    "timeout": 30,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@",
-      "@@@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 (2).change data.changes",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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=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)@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).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,
-    "name": "checkout pigweed (2).cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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,
-    "name": "checkout pigweed (2).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]/project"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000028.0
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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 (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git checkout patch",
-    "timeout": 600.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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git remote add",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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/main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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/main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git set upstream",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.post-rebase log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).apply pigweed:1234.timeout 10s (3)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "--detach"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.detach",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.reattach",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python3",
-      "RECIPE_MODULE[pigweed::checkout]/resources/submodule_status.py",
-      "[START_DIR]/project",
-      "/path/to/tmp/json",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).submodule status",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@{}@@@",
-      "@@@STEP_LOG_END@json.output@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]",
-      "[CLEANUP]/tmp_tmp_1"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]@@@",
-      "@@@STEP_LOG_END@tmp_tmp_1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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]/project",
-    "name": "checkout pigweed (2).git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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=None, branch='main', 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=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,
-    "name": "checkout pigweed (2).mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).submodule-status",
-    "timeout": 600.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,
-    "name": "checkout pigweed (2).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]/project",
-    "name": "checkout pigweed (2).log",
-    "timeout": 600.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,
-    "name": "checkout pigweed (2).write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
     "cmd": [
       "git",
       "log",
@@ -1527,7 +965,7 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "[START_DIR]/co/WORKSPACE",
+      "[START_DIR]/checkout/WORKSPACE",
       "/path/to/tmp/"
     ],
     "infra_step": true,
@@ -1709,7 +1147,7 @@
       "/path/to/tmp/json",
       "copy",
       "git_repository(\n    name = \"other-repo\"\n    remote = \"https://pigweed.googlesource.com/other/repo.git\",\n    commit = \"invalid commit line won't be found\",\n)\n\ngit_repository(\n    module_name = \"pigweed\",\n    # ROLL: Warning: this entry is automatically updated.\n    # ROLL: Last updated 2012-05-14.\n    # ROLL: By https://cr-buildbucket.appspot.com/build/0.\n    commit = \"2222222222222222222222222222222222222222\",\n    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",\n    git_repository_attribute_test = \"ignored\",\n    strip_prefix = \"pw_toolchain_bazel\",\n)\n\ngit_repository(\n    name = \"missing final quote/comma so will miss this line\n    remote = \"https://pigweed.googlesource.com/third/repo.git\",\n    commit = \"2222222222222222222222222222222222222222\",\n)\n",
-      "[START_DIR]/co/WORKSPACE"
+      "[START_DIR]/checkout/WORKSPACE"
     ],
     "infra_step": true,
     "name": "write new WORKSPACE",
@@ -1786,6 +1224,10 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "nothing to roll, exiting"
+  },
+  {
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipe_modules/bazel_roll/tests/full.expected/bad-trigger.json b/recipe_modules/bazel_roll/tests/full.expected/bad-trigger.json
new file mode 100644
index 0000000..debd8e7
--- /dev/null
+++ b/recipe_modules/bazel_roll/tests/full.expected/bad-trigger.json
@@ -0,0 +1,16 @@
+[
+  {
+    "cmd": [],
+    "name": "triggering repository (https://pigweed.googlesource.com/bar) does not match project remote (https://pigweed.googlesource.com/foo)",
+    "~followup_annotations": [
+      "@@@STEP_FAILURE@@@"
+    ]
+  },
+  {
+    "failure": {
+      "failure": {},
+      "humanReason": "Step('triggering repository (https://pigweed.googlesource.com/bar) does not match project remote (https://pigweed.googlesource.com/foo)') (retcode: 0)"
+    },
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipes/bazel_roller.expected/backwards.json b/recipe_modules/bazel_roll/tests/full.expected/no-trigger.json
similarity index 71%
rename from recipes/bazel_roller.expected/backwards.json
rename to recipe_modules/bazel_roll/tests/full.expected/no-trigger.json
index ef4f845..3d603a6 100644
--- a/recipes/bazel_roller.expected/backwards.json
+++ b/recipe_modules/bazel_roll/tests/full.expected/no-trigger.json
@@ -1,14 +1,17 @@
 [
   {
     "cmd": [],
-    "name": "checkout pigweed"
+    "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\n@@@"
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nuse_trigger: true\n@@@"
     ]
   },
   {
@@ -16,7 +19,165 @@
     "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: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nmatch_branch: true\nsubmodule_timeout_sec: 600\n@@@"
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nmanifest_file: \"default.xml\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\nuse_trigger: true\n@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "ls-remote",
+      "--heads",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "main"
+    ],
+    "name": "checkout pigweed.change data.git ls-remote",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_LOG_LINE@stdout@h3ll0\trefs/heads/main@@@",
+      "@@@STEP_LOG_END@stdout@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.change data.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,
+    "name": "checkout pigweed.change data.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": "checkout pigweed.change data.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,
+    "name": "checkout pigweed.change data.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,
+    "name": "checkout pigweed.change data.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\": \"commit:h3ll0\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "checkout pigweed.change data.number",
+    "timeout": 30,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@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=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)@@@"
     ]
   },
   {
@@ -146,7 +307,7 @@
       "--jobs",
       "4",
       "origin",
-      "--recurse-submodules"
+      "--no-recurse-submodules"
     ],
     "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
     "infra_step": true,
@@ -171,21 +332,6 @@
     ]
   },
   {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync",
-      "--recursive"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git submodule sync",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
     "cmd": [],
     "name": "checkout pigweed.cache.timeout 10s (2)",
     "~followup_annotations": [
@@ -197,7 +343,6 @@
       "git",
       "submodule",
       "update",
-      "--init",
       "--recursive",
       "--force",
       "--jobs",
@@ -237,7 +382,7 @@
       "copytree",
       "--symlinks",
       "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-      "[START_DIR]/co"
+      "[START_DIR]/project"
     ],
     "infra_step": true,
     "name": "checkout pigweed.copy from cache",
@@ -269,7 +414,7 @@
       "ensure-directory",
       "--mode",
       "0o777",
-      "[START_DIR]/co"
+      "[START_DIR]/project"
     ],
     "infra_step": true,
     "luci_context": {
@@ -288,7 +433,7 @@
       "git",
       "init"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git init",
     "timeout": 300.0,
@@ -304,7 +449,7 @@
       "origin",
       "https://pigweed.googlesource.com/pigweed/pigweed"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git remote",
     "timeout": 600.0,
@@ -319,7 +464,7 @@
       "core.longpaths",
       "true"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.set core.longpaths",
     "timeout": 300.0,
@@ -334,7 +479,7 @@
       "fetch.uriprotocols",
       "https"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.set fetch.uriprotocols",
     "timeout": 300.0,
@@ -350,10 +495,9 @@
       "--jobs",
       "4",
       "origin",
-      "main",
-      "--recurse-submodules"
+      "main"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git fetch",
     "timeout": 1200.0,
@@ -368,7 +512,7 @@
       "-f",
       "FETCH_HEAD"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git checkout",
     "timeout": 600.0,
@@ -382,7 +526,7 @@
       "rev-parse",
       "HEAD"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git rev-parse",
     "timeout": 300.0,
@@ -398,7 +542,7 @@
       "-d",
       "-x"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
     "name": "checkout pigweed.git checkout.git clean",
     "timeout": 600.0,
@@ -407,8 +551,30 @@
     ]
   },
   {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/project",
+    "name": "checkout pigweed.git rev-parse",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
     "cmd": [],
-    "name": "checkout pigweed.git checkout.submodule",
+    "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@@@"
     ]
@@ -416,15 +582,139 @@
   {
     "cmd": [
       "git",
-      "submodule",
-      "sync"
+      "fetch",
+      "--jobs",
+      "4",
+      "https://pigweed.googlesource.com/pigweed/pigweed",
+      "h3ll0",
+      "--no-recurse-submodules"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "infra_step": true,
-    "name": "checkout pigweed.git checkout.submodule.git submodule sync",
+    "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]/project",
+    "infra_step": true,
+    "name": "checkout pigweed.apply pigweed:1234.git checkout patch",
     "timeout": 600.0,
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "remote",
+      "add",
+      "https___pigweed_googlesource_com_pigweed_pigweed",
+      "https://pigweed.googlesource.com/pigweed/pigweed"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "name": "checkout pigweed.apply pigweed:1234.git remote add",
+    "timeout": 600.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/main"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "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/main"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "name": "checkout pigweed.apply pigweed:1234.git set upstream",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/project",
+    "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]/project",
+    "infra_step": true,
+    "name": "checkout pigweed.apply pigweed:1234.post-rebase log",
+    "timeout": 600.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]/project",
+    "name": "checkout pigweed.apply pigweed:1234.detach",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
     ]
   },
   {
@@ -434,16 +724,73 @@
       "update",
       "--init",
       "--recursive",
-      "--force",
       "--jobs",
       "4"
     ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.submodule.git submodule update",
+    "cwd": "[START_DIR]/project",
+    "name": "checkout pigweed.apply pigweed:1234.git submodule update",
     "timeout": 600,
     "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-"
+    ],
+    "cwd": "[START_DIR]/project",
+    "name": "checkout pigweed.apply pigweed:1234.reattach",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "RECIPE_MODULE[pigweed::checkout]/resources/submodule_status.py",
+      "[START_DIR]/project",
+      "/path/to/tmp/json",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/project",
+    "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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]",
+      "[CLEANUP]/tmp_tmp_1"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]@@@",
+      "@@@STEP_LOG_END@tmp_tmp_1@@@"
     ]
   },
   {
@@ -461,14 +808,23 @@
       "-n",
       "10"
     ],
-    "cwd": "[START_DIR]/co",
-    "name": "checkout pigweed.git log.[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
+    "name": "checkout pigweed.git log.[START_DIR]/project",
     "timeout": 600.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",
@@ -476,7 +832,7 @@
       "-f",
       "-d"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "name": "checkout pigweed.git clean",
     "timeout": 600.0,
     "~followup_annotations": [
@@ -488,7 +844,7 @@
       "git",
       "status"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "name": "checkout pigweed.git status",
     "timeout": 600.0,
     "~followup_annotations": [
@@ -496,6 +852,14 @@
     ]
   },
   {
+    "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=None, branch='main', 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=None)]\nnot applied []@@@"
+    ]
+  },
+  {
     "cmd": [
       "vpython3",
       "-u",
@@ -520,7 +884,7 @@
       "status",
       "--recursive"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "name": "checkout pigweed.submodule-status",
     "timeout": 600.0,
     "~followup_annotations": [
@@ -554,7 +918,7 @@
       "-n",
       "10"
     ],
-    "cwd": "[START_DIR]/co",
+    "cwd": "[START_DIR]/project",
     "name": "checkout pigweed.log",
     "timeout": 600.0,
     "~followup_annotations": [
@@ -580,932 +944,6 @@
     ]
   },
   {
-    "cmd": [],
-    "name": "checkout pigweed (2)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@applied pigweed:1234 (.)@https://pigweed-review.googlesource.com/c/1234@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nuse_trigger: true\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\nuse_trigger: true\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "ls-remote",
-      "--heads",
-      "https://pigweed.googlesource.com/pigweed/pigweed",
-      "main"
-    ],
-    "name": "checkout pigweed (2).change data.git ls-remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@",
-      "@@@STEP_LOG_LINE@stdout@h3ll0\trefs/heads/main@@@",
-      "@@@STEP_LOG_END@stdout@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data.ensure infra/tools/luci/gerrit/${platform}",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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\": \"commit:h3ll0\"}}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).change data.number",
-    "timeout": 30,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@",
-      "@@@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 (2).change data.changes",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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=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)@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).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,
-    "name": "checkout pigweed (2).cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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,
-    "name": "checkout pigweed (2).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]/project"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000028.0
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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 (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git checkout patch",
-    "timeout": 600.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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git remote add",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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/main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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/main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git set upstream",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.post-rebase log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).apply pigweed:1234.timeout 10s (3)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "--detach"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.detach",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.reattach",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python3",
-      "RECIPE_MODULE[pigweed::checkout]/resources/submodule_status.py",
-      "[START_DIR]/project",
-      "/path/to/tmp/json",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).submodule status",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@{}@@@",
-      "@@@STEP_LOG_END@json.output@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]",
-      "[CLEANUP]/tmp_tmp_1"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]@@@",
-      "@@@STEP_LOG_END@tmp_tmp_1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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]/project",
-    "name": "checkout pigweed (2).git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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=None, branch='main', 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=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,
-    "name": "checkout pigweed (2).mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).submodule-status",
-    "timeout": 600.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,
-    "name": "checkout pigweed (2).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]/project",
-    "name": "checkout pigweed (2).log",
-    "timeout": 600.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,
-    "name": "checkout pigweed (2).write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
     "cmd": [
       "git",
       "log",
@@ -1527,7 +965,7 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "[START_DIR]/co/WORKSPACE",
+      "[START_DIR]/checkout/WORKSPACE",
       "/path/to/tmp/"
     ],
     "infra_step": true,
@@ -1709,7 +1147,7 @@
       "/path/to/tmp/json",
       "copy",
       "git_repository(\n    name = \"other-repo\"\n    remote = \"https://pigweed.googlesource.com/other/repo.git\",\n    commit = \"invalid commit line won't be found\",\n)\n\ngit_repository(\n    module_name = \"pigweed\",\n    # ROLL: Warning: this entry is automatically updated.\n    # ROLL: Last updated 2012-05-14.\n    # ROLL: By https://cr-buildbucket.appspot.com/build/0.\n    commit = \"2222222222222222222222222222222222222222\",\n    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",\n    git_repository_attribute_test = \"ignored\",\n    strip_prefix = \"pw_toolchain_bazel\",\n)\n\ngit_repository(\n    name = \"missing final quote/comma so will miss this line\n    remote = \"https://pigweed.googlesource.com/third/repo.git\",\n    commit = \"2222222222222222222222222222222222222222\",\n)\n",
-      "[START_DIR]/co/WORKSPACE"
+      "[START_DIR]/checkout/WORKSPACE"
     ],
     "infra_step": true,
     "name": "write new WORKSPACE",
@@ -1743,7 +1181,7 @@
     "cmd": [],
     "name": "get roll direction",
     "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@backward@@@"
+      "@@@STEP_SUMMARY_TEXT@forward@@@"
     ]
   },
   {
@@ -1778,11 +1216,129 @@
   },
   {
     "cmd": [],
-    "name": "cancelling roll",
+    "name": "remote"
+  },
+  {
+    "cmd": [
+      "git",
+      "remote"
+    ],
+    "cwd": "[START_DIR]/project",
+    "name": "remote.name",
+    "timeout": 600.0,
     "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@not updating from 1111111 to 2222222 because 1111111 is newer than 2222222@@@",
-      "@@@STEP_LINK@1111111111111111111111111111111111111111@https://pigweed.googlesource.com/pigweed/pigweed/+/1111111111111111111111111111111111111111@@@",
-      "@@@STEP_LINK@2222222222222222222222222222222222222222@https://pigweed.googlesource.com/pigweed/pigweed/+/2222222222222222222222222222222222222222@@@"
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "remote",
+      "get-url",
+      "origin"
+    ],
+    "cwd": "[START_DIR]/project",
+    "name": "remote.url",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "pigweed"
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--pretty=format:%H\n%an\n%ae\n%B",
+      "-z",
+      "1111111111111111111111111111111111111111..2222222222222222222222222222222222222222"
+    ],
+    "cwd": "[START_DIR]/project",
+    "name": "pigweed.git log",
+    "timeout": 600.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\": {\"q\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "name": "pigweed.get change-id",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": 12345@@@",
+      "@@@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:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"@@@",
+      "@@@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-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"12345\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "name": "pigweed.get 12345",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"owner\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"author@example.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"author\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"reviewers\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"REVIEWER\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"email\": \"reviewer@example.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"name\": \"reviewer\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"email\": \"nobody@google.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"name\": \"nobody\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"email\": \"robot@gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"name\": \"robot\"@@@",
+      "@@@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@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"12345\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/12345@@@"
     ]
   },
   {
diff --git a/recipe_modules/bazel_roll/tests/full.expected/success.json b/recipe_modules/bazel_roll/tests/full.expected/success.json
new file mode 100644
index 0000000..1373798
--- /dev/null
+++ b/recipe_modules/bazel_roll/tests/full.expected/success.json
@@ -0,0 +1,1481 @@
+[
+  {
+    "cmd": [],
+    "name": "checkout pigweed"
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.options",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\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\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\n@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.not matching branch names",
+    "~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": [
+      "git",
+      "init"
+    ],
+    "cwd": "[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.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",
+    "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",
+    "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.set fetch.uriprotocols",
+    "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",
+      "--no-recurse-submodules"
+    ],
+    "cwd": "[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.git fetch",
+    "timeout": 1200.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "merge",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[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.git merge",
+    "timeout": 600.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",
+      "--recursive",
+      "--force",
+      "--jobs",
+      "4"
+    ],
+    "cwd": "[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.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]/project"
+    ],
+    "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]/project"
+    ],
+    "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]/project",
+    "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]/project",
+    "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": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "config",
+      "core.longpaths",
+      "true"
+    ],
+    "cwd": "[START_DIR]/project",
+    "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]/project",
+    "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 fetch.uriprotocols",
+    "timeout": 300.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "--tags",
+      "--jobs",
+      "4",
+      "origin",
+      "main"
+    ],
+    "cwd": "[START_DIR]/project",
+    "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]/project",
+    "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": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/project",
+    "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]/project",
+    "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": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "checkout pigweed.git log",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--oneline",
+      "-n",
+      "10"
+    ],
+    "cwd": "[START_DIR]/project",
+    "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]/project",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-f",
+      "-d"
+    ],
+    "cwd": "[START_DIR]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git clean",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "status"
+    ],
+    "cwd": "[START_DIR]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.git status",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "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]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.submodule-status",
+    "timeout": 600.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]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "checkout pigweed.log",
+    "timeout": 600.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": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "[START_DIR]/checkout/WORKSPACE",
+      "/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": "read old WORKSPACE",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"other-repo\"@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"invalid commit line won't be found\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    module_name = \"pigweed\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Multiple@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: roll@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: comment@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: lines!@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"1111111111111111111111111111111111111111\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    git_repository_attribute_test = \"ignored\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    strip_prefix = \"pw_toolchain_bazel\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"missing final quote/comma so will miss this line@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_END@WORKSPACE@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "found other remote 'https://pigweed.googlesource.com/other/repo.git'",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@lines@2 @@@",
+      "@@@STEP_LOG_LINE@lines@3 @@@",
+      "@@@STEP_LOG_LINE@lines@4 @@@",
+      "@@@STEP_LOG_LINE@lines@5 @@@",
+      "@@@STEP_LOG_LINE@lines@6 @@@",
+      "@@@STEP_LOG_LINE@lines@7 @@@",
+      "@@@STEP_LOG_LINE@lines@8 @@@",
+      "@@@STEP_LOG_LINE@lines@9 @@@",
+      "@@@STEP_LOG_LINE@lines@10 git_repository(@@@",
+      "@@@STEP_LOG_LINE@lines@11     name = \"other-repo\"@@@",
+      "@@@STEP_LOG_LINE@lines@12     remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@lines@13     commit = \"invalid commit line won't be found\",@@@",
+      "@@@STEP_LOG_LINE@lines@14 )@@@",
+      "@@@STEP_LOG_LINE@lines@15 @@@",
+      "@@@STEP_LOG_LINE@lines@16 git_repository(@@@",
+      "@@@STEP_LOG_LINE@lines@17     module_name = \"pigweed\",@@@",
+      "@@@STEP_LOG_LINE@lines@18     # ROLL: Multiple@@@",
+      "@@@STEP_LOG_LINE@lines@19     # ROLL: roll@@@",
+      "@@@STEP_LOG_LINE@lines@20     # ROLL: comment@@@",
+      "@@@STEP_LOG_LINE@lines@21     # ROLL: lines!@@@",
+      "@@@STEP_LOG_LINE@lines@22     commit = \"1111111111111111111111111111111111111111\",@@@",
+      "@@@STEP_LOG_END@lines@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "found equivalent remote 'https://pigweed.googlesource.com/pigweed/pigweed.git'",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@lines@13     commit = \"invalid commit line won't be found\",@@@",
+      "@@@STEP_LOG_LINE@lines@14 )@@@",
+      "@@@STEP_LOG_LINE@lines@15 @@@",
+      "@@@STEP_LOG_LINE@lines@16 git_repository(@@@",
+      "@@@STEP_LOG_LINE@lines@17     module_name = \"pigweed\",@@@",
+      "@@@STEP_LOG_LINE@lines@18     # ROLL: Multiple@@@",
+      "@@@STEP_LOG_LINE@lines@19     # ROLL: roll@@@",
+      "@@@STEP_LOG_LINE@lines@20     # ROLL: comment@@@",
+      "@@@STEP_LOG_LINE@lines@21     # ROLL: lines!@@@",
+      "@@@STEP_LOG_LINE@lines@22     commit = \"1111111111111111111111111111111111111111\",@@@",
+      "@@@STEP_LOG_LINE@lines@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
+      "@@@STEP_LOG_LINE@lines@24     git_repository_attribute_test = \"ignored\",@@@",
+      "@@@STEP_LOG_LINE@lines@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
+      "@@@STEP_LOG_LINE@lines@26 )@@@",
+      "@@@STEP_LOG_LINE@lines@27 @@@",
+      "@@@STEP_LOG_LINE@lines@28 git_repository(@@@",
+      "@@@STEP_LOG_LINE@lines@29     name = \"missing final quote/comma so will miss this line@@@",
+      "@@@STEP_LOG_LINE@lines@30     remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@lines@31     commit = \"2222222222222222222222222222222222222222\",@@@",
+      "@@@STEP_LOG_LINE@lines@32 )@@@",
+      "@@@STEP_LOG_LINE@lines@33 @@@",
+      "@@@STEP_LOG_END@lines@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "found other remote 'https://pigweed.googlesource.com/third/repo.git'",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@lines@20     # ROLL: comment@@@",
+      "@@@STEP_LOG_LINE@lines@21     # ROLL: lines!@@@",
+      "@@@STEP_LOG_LINE@lines@22     commit = \"1111111111111111111111111111111111111111\",@@@",
+      "@@@STEP_LOG_LINE@lines@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
+      "@@@STEP_LOG_LINE@lines@24     git_repository_attribute_test = \"ignored\",@@@",
+      "@@@STEP_LOG_LINE@lines@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
+      "@@@STEP_LOG_LINE@lines@26 )@@@",
+      "@@@STEP_LOG_LINE@lines@27 @@@",
+      "@@@STEP_LOG_LINE@lines@28 git_repository(@@@",
+      "@@@STEP_LOG_LINE@lines@29     name = \"missing final quote/comma so will miss this line@@@",
+      "@@@STEP_LOG_LINE@lines@30     remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@lines@31     commit = \"2222222222222222222222222222222222222222\",@@@",
+      "@@@STEP_LOG_LINE@lines@32 )@@@",
+      "@@@STEP_LOG_LINE@lines@33 @@@",
+      "@@@STEP_LOG_LINE@lines@34 @@@",
+      "@@@STEP_LOG_LINE@lines@35 @@@",
+      "@@@STEP_LOG_LINE@lines@36 @@@",
+      "@@@STEP_LOG_LINE@lines@37 @@@",
+      "@@@STEP_LOG_LINE@lines@38 @@@",
+      "@@@STEP_LOG_LINE@lines@39 @@@",
+      "@@@STEP_LOG_LINE@lines@40 @@@",
+      "@@@STEP_LOG_END@lines@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "lines",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@0_center@23@@@",
+      "@@@STEP_LOG_END@0_center@@@",
+      "@@@STEP_LOG_LINE@1_orig@13     commit = \"invalid commit line won't be found\",@@@",
+      "@@@STEP_LOG_LINE@1_orig@14 )@@@",
+      "@@@STEP_LOG_LINE@1_orig@15 @@@",
+      "@@@STEP_LOG_LINE@1_orig@16 git_repository(@@@",
+      "@@@STEP_LOG_LINE@1_orig@17     module_name = \"pigweed\",@@@",
+      "@@@STEP_LOG_LINE@1_orig@18     # ROLL: Multiple@@@",
+      "@@@STEP_LOG_LINE@1_orig@19     # ROLL: roll@@@",
+      "@@@STEP_LOG_LINE@1_orig@20     # ROLL: comment@@@",
+      "@@@STEP_LOG_LINE@1_orig@21     # ROLL: lines!@@@",
+      "@@@STEP_LOG_LINE@1_orig@22     commit = \"1111111111111111111111111111111111111111\",@@@",
+      "@@@STEP_LOG_LINE@1_orig@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
+      "@@@STEP_LOG_LINE@1_orig@24     git_repository_attribute_test = \"ignored\",@@@",
+      "@@@STEP_LOG_LINE@1_orig@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
+      "@@@STEP_LOG_LINE@1_orig@26 )@@@",
+      "@@@STEP_LOG_LINE@1_orig@27 @@@",
+      "@@@STEP_LOG_LINE@1_orig@28 git_repository(@@@",
+      "@@@STEP_LOG_LINE@1_orig@29     name = \"missing final quote/comma so will miss this line@@@",
+      "@@@STEP_LOG_LINE@1_orig@30     remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@1_orig@31     commit = \"2222222222222222222222222222222222222222\",@@@",
+      "@@@STEP_LOG_LINE@1_orig@32 )@@@",
+      "@@@STEP_LOG_LINE@1_orig@33 @@@",
+      "@@@STEP_LOG_END@1_orig@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@15 @@@",
+      "@@@STEP_LOG_LINE@2_trimmed@16 git_repository(@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@17     module_name = \"pigweed\",@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@18     # ROLL: Multiple@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@19     # ROLL: roll@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@20     # ROLL: comment@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@21     # ROLL: lines!@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@22     commit = \"1111111111111111111111111111111111111111\",@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@24     git_repository_attribute_test = \"ignored\",@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
+      "@@@STEP_LOG_LINE@2_trimmed@26 )@@@",
+      "@@@STEP_LOG_END@2_trimmed@@@",
+      "@@@STEP_LOG_LINE@3_sorted@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
+      "@@@STEP_LOG_LINE@3_sorted@22     commit = \"1111111111111111111111111111111111111111\",@@@",
+      "@@@STEP_LOG_LINE@3_sorted@24     git_repository_attribute_test = \"ignored\",@@@",
+      "@@@STEP_LOG_LINE@3_sorted@21     # ROLL: lines!@@@",
+      "@@@STEP_LOG_LINE@3_sorted@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
+      "@@@STEP_LOG_LINE@3_sorted@20     # ROLL: comment@@@",
+      "@@@STEP_LOG_LINE@3_sorted@26 )@@@",
+      "@@@STEP_LOG_LINE@3_sorted@19     # ROLL: roll@@@",
+      "@@@STEP_LOG_LINE@3_sorted@18     # ROLL: Multiple@@@",
+      "@@@STEP_LOG_LINE@3_sorted@17     module_name = \"pigweed\",@@@",
+      "@@@STEP_LOG_LINE@3_sorted@16 git_repository(@@@",
+      "@@@STEP_LOG_LINE@3_sorted@15 @@@",
+      "@@@STEP_LOG_END@3_sorted@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "vpython3",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "copy",
+      "git_repository(\n    name = \"other-repo\"\n    remote = \"https://pigweed.googlesource.com/other/repo.git\",\n    commit = \"invalid commit line won't be found\",\n)\n\ngit_repository(\n    module_name = \"pigweed\",\n    # ROLL: Warning: this entry is automatically updated.\n    # ROLL: Last updated 2012-05-14.\n    # ROLL: By https://cr-buildbucket.appspot.com/build/8945511751514863184.\n    commit = \"2d72510e447ab60a9728aeea2362d8be2cbd7789\",\n    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",\n    git_repository_attribute_test = \"ignored\",\n    strip_prefix = \"pw_toolchain_bazel\",\n)\n\ngit_repository(\n    name = \"missing final quote/comma so will miss this line\n    remote = \"https://pigweed.googlesource.com/third/repo.git\",\n    commit = \"2222222222222222222222222222222222222222\",\n)\n",
+      "[START_DIR]/checkout/WORKSPACE"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "write new WORKSPACE",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"other-repo\"@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"invalid commit line won't be found\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    module_name = \"pigweed\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Warning: this entry is automatically updated.@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Last updated 2012-05-14.@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: By https://cr-buildbucket.appspot.com/build/8945511751514863184.@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2d72510e447ab60a9728aeea2362d8be2cbd7789\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    git_repository_attribute_test = \"ignored\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    strip_prefix = \"pw_toolchain_bazel\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"missing final quote/comma so will miss this line@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
+      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
+      "@@@STEP_LOG_END@WORKSPACE@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "get roll direction",
+    "~followup_annotations": [
+      "@@@STEP_SUMMARY_TEXT@forward@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "merge-base",
+      "--is-ancestor",
+      "1111111111111111111111111111111111111111",
+      "2d72510e447ab60a9728aeea2362d8be2cbd7789"
+    ],
+    "cwd": "[START_DIR]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get roll direction.is forward",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "merge-base",
+      "--is-ancestor",
+      "2d72510e447ab60a9728aeea2362d8be2cbd7789",
+      "1111111111111111111111111111111111111111"
+    ],
+    "cwd": "[START_DIR]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get roll direction.is backward",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "remote"
+  },
+  {
+    "cmd": [
+      "git",
+      "remote"
+    ],
+    "cwd": "[START_DIR]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "remote.name",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "remote",
+      "get-url",
+      "origin"
+    ],
+    "cwd": "[START_DIR]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "remote.url",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "pigweed"
+  },
+  {
+    "cmd": [
+      "git",
+      "log",
+      "--pretty=format:%H\n%an\n%ae\n%B",
+      "-z",
+      "1111111111111111111111111111111111111111..2d72510e447ab60a9728aeea2362d8be2cbd7789"
+    ],
+    "cwd": "[START_DIR]/project",
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "pigweed.git log",
+    "timeout": 600.0,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "pigweed.ensure infra/tools/luci/gerrit/${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::gerrit]/resources/cipd.ensure",
+      "/path/to/tmp/"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.get packages.read ensure file",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@",
+      "@@@STEP_LOG_LINE@cipd.ensure@infra/tools/luci/gerrit/${platform} version:pinned-version@@@",
+      "@@@STEP_LOG_END@cipd.ensure@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit",
+    "~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/gerrit/0e548aa33f8113a45a5b3b62201e114e98e63d00f97296912380138f44597b07"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@3@@@"
+    ]
+  },
+  {
+    "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"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "pigweed.ensure infra/tools/luci/gerrit/${platform}.install infra/tools/luci/gerrit.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/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\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "pigweed.get change-id",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@[@@@",
+      "@@@STEP_LOG_LINE@json.output@  {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"_number\": 12345@@@",
+      "@@@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:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"@@@",
+      "@@@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-detail",
+      "-host",
+      "https://pigweed-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"12345\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "cwd": "[START_DIR]/project",
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "project:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "pigweed.get 12345",
+    "timeout": 600,
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"owner\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"author@example.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"author\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  },@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"reviewers\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"REVIEWER\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"email\": \"reviewer@example.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"name\": \"reviewer\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"email\": \"nobody@google.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"name\": \"nobody\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      },@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"email\": \"robot@gserviceaccount.com\",@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"name\": \"robot\"@@@",
+      "@@@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@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"12345\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@",
+      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/12345@@@"
+    ]
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipes/bazel_roller.expected/unrecognized-remote.json b/recipe_modules/bazel_roll/tests/full.expected/unrecognized-remote.json
similarity index 68%
rename from recipes/bazel_roller.expected/unrecognized-remote.json
rename to recipe_modules/bazel_roll/tests/full.expected/unrecognized-remote.json
index 5617b2b..4c39376 100644
--- a/recipes/bazel_roller.expected/unrecognized-remote.json
+++ b/recipe_modules/bazel_roll/tests/full.expected/unrecognized-remote.json
@@ -1,956 +1,6 @@
 [
   {
     "cmd": [],
-    "name": "checkout pigweed"
-  },
-  {
-    "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\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: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nmatch_branch: true\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.not matching branch names",
-    "~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": [
-      "git",
-      "init"
-    ],
-    "cwd": "[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.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",
-    "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",
-    "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.set fetch.uriprotocols",
-    "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",
-    "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",
-    "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",
-    "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": 600.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",
-    "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",
-    "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",
-    "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": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "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.git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "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.git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main",
-      "--recurse-submodules"
-    ],
-    "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.git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "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.git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "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.git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "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.git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout.submodule",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "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.git checkout.submodule.git submodule sync",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "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.git checkout.submodule.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "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.git log.[START_DIR]/co",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "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.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "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.git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "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",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.submodule-status",
-    "timeout": 600.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",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.log",
-    "timeout": 600.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": "checkout unrecognized-remote",
     "~followup_annotations": [
       "@@@STEP_LINK@applied pigweed:1234 (.)@https://pigweed-review.googlesource.com/c/1234@@@"
@@ -1191,6 +241,36 @@
       "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 unrecognized-remote.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"
@@ -1511,7 +591,7 @@
     "luci_context": {
       "deadline": {
         "grace_period": 30.0,
-        "soft_deadline": 1337000028.0
+        "soft_deadline": 1337000019.0
       },
       "realm": {
         "name": "project:ci"
@@ -2400,7 +1480,7 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "[START_DIR]/co/WORKSPACE",
+      "[START_DIR]/checkout/WORKSPACE",
       "/path/to/tmp/"
     ],
     "infra_step": true,
@@ -2531,7 +1611,7 @@
   },
   {
     "cmd": [],
-    "name": "could not find remote https://pigweed.googlesource.com/unrecognized-remote in [START_DIR]/co/WORKSPACE"
+    "name": "could not find remote https://pigweed.googlesource.com/unrecognized-remote in [START_DIR]/checkout/WORKSPACE"
   },
   {
     "cmd": [],
diff --git a/recipe_modules/bazel_roll/tests/full.proto b/recipe_modules/bazel_roll/tests/full.proto
new file mode 100644
index 0000000..97d631e
--- /dev/null
+++ b/recipe_modules/bazel_roll/tests/full.proto
@@ -0,0 +1,24 @@
+// Copyright 2021 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+//     https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+syntax = "proto3";
+
+package recipe_modules.pigweed.bazel_roll.tests;
+
+import "recipe_modules/pigweed/bazel_roll/git_repository.proto";
+
+message InputProperties {
+  // Submodules to update.
+  repeated recipe_modules.pigweed.bazel_roll.GitRepository git_repositories = 1;
+}
diff --git a/recipe_modules/bazel_roll/tests/full.py b/recipe_modules/bazel_roll/tests/full.py
new file mode 100644
index 0000000..a31a8a3
--- /dev/null
+++ b/recipe_modules/bazel_roll/tests/full.py
@@ -0,0 +1,122 @@
+# Copyright 2024 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+from __future__ import annotations
+
+from PB.recipe_modules.pigweed.bazel_roll.tests.full import InputProperties
+from recipe_engine import post_process
+
+DEPS = [
+    "pigweed/bazel_roll",
+    "pigweed/checkout",
+    "pigweed/roll_util",
+    "recipe_engine/properties",
+    "recipe_engine/step",
+]
+
+PROPERTIES = InputProperties
+
+
+def RunSteps(  # pylint: disable=invalid-name
+    api: recipe_api.RecipeScriptApi,
+    props: InputProperties,
+):
+    checkout = api.checkout.fake_context()
+
+    assert len(props.git_repositories) == 1
+
+    rolls = api.bazel_roll.update_git_repository(
+        checkout=checkout,
+        git_repository=props.git_repositories[0],
+    )
+
+    if not rolls:
+        with api.step.nest('nothing to roll, exiting'):
+            return
+
+
+def GenTests(api) -> Generator[recipe_test_api.TestData, None, None]:
+    """Create tests."""
+
+    def _url(x: str = 'pigweed/pigweed'):
+        assert ':' not in x
+        return f'https://pigweed.googlesource.com/{x}'
+
+    def trigger(url, **kwargs):
+        return api.checkout.ci_test_data(git_repo=_url(url), **kwargs)
+
+    def properties(repo: GitRepository, **kwargs):
+        props = InputProperties(**kwargs)
+        props.git_repositories.append(repo)
+        return api.properties(props)
+
+    def commit_data(name, **kwargs):
+        return api.roll_util.commit_data(
+            name,
+            api.roll_util.commit('a' * 40, 'foo\nbar\n\nChange-Id: I1111'),
+            **kwargs,
+        )
+
+    yield api.test(
+        'success',
+        properties(api.bazel_roll.git_repo()),
+        api.roll_util.properties(commit_divider='--divider--'),
+        trigger('pigweed/pigweed'),
+        api.roll_util.forward_roll(),
+        commit_data('pigweed', prefix=''),
+    )
+
+    yield api.test(
+        'unrecognized-remote',
+        properties(api.bazel_roll.git_repo(remote=_url('unrecognized-remote'))),
+        api.roll_util.properties(commit_divider='--divider--'),
+        trigger('unrecognized-remote'),
+        api.post_process(post_process.MustRun, 'failed to update commit hash'),
+        status='FAILURE',
+    )
+
+    yield api.test(
+        'bad-trigger',
+        properties(
+            api.bazel_roll.git_repo(
+                remote=_url('foo'),
+                workspace_path='bar/WORKSPACE',
+            ),
+        ),
+        trigger('bar'),
+        status='FAILURE',
+    )
+
+    yield api.test(
+        'name-not-found',
+        properties(api.bazel_roll.git_repo(remote=_url('third/repo'))),
+        trigger('third/repo'),
+        api.roll_util.forward_roll(),
+        api.post_process(post_process.MustRunRE, r'could not find name.*'),
+        api.post_process(post_process.DropExpectation),
+        status='FAILURE',
+    )
+
+    yield api.test(
+        'no-trigger',
+        properties(api.bazel_roll.git_repo()),
+        api.roll_util.forward_roll(),
+        commit_data('pigweed', prefix=''),
+    )
+
+    yield api.test(
+        'backwards',
+        properties(api.bazel_roll.git_repo()),
+        api.roll_util.backward_roll(),
+    )
diff --git a/recipes/bazel_roller.expected/bad-trigger.json b/recipes/bazel_roller.expected/bad-trigger.json
deleted file mode 100644
index 8abea5f..0000000
--- a/recipes/bazel_roller.expected/bad-trigger.json
+++ /dev/null
@@ -1,966 +0,0 @@
-[
-  {
-    "cmd": [],
-    "name": "checkout pigweed"
-  },
-  {
-    "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\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: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nmatch_branch: true\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.not matching branch names",
-    "~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": [
-      "git",
-      "init"
-    ],
-    "cwd": "[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.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",
-    "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",
-    "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.set fetch.uriprotocols",
-    "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",
-    "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",
-    "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",
-    "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": 600.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",
-    "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",
-    "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",
-    "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": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "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.git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "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.git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main",
-      "--recurse-submodules"
-    ],
-    "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.git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "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.git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "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.git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "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.git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout.submodule",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "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.git checkout.submodule.git submodule sync",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "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.git checkout.submodule.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "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.git log.[START_DIR]/co",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "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.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "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.git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "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",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.submodule-status",
-    "timeout": 600.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",
-    "luci_context": {
-      "realm": {
-        "name": "project:ci"
-      },
-      "resultdb": {
-        "current_invocation": {
-          "name": "invocations/build:8945511751514863184",
-          "update_token": "token"
-        },
-        "hostname": "rdbhost"
-      }
-    },
-    "name": "checkout pigweed.log",
-    "timeout": 600.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": "triggering repository (https://pigweed.googlesource.com/bar) does not match project remote (https://pigweed.googlesource.com/foo)",
-    "~followup_annotations": [
-      "@@@STEP_FAILURE@@@"
-    ]
-  },
-  {
-    "failure": {
-      "failure": {},
-      "humanReason": "Step('triggering repository (https://pigweed.googlesource.com/bar) does not match project remote (https://pigweed.googlesource.com/foo)') (retcode: 0)"
-    },
-    "name": "$result"
-  }
-]
\ No newline at end of file
diff --git a/recipes/bazel_roller.expected/no-trigger.json b/recipes/bazel_roller.expected/no-trigger.json
deleted file mode 100644
index edc7264..0000000
--- a/recipes/bazel_roller.expected/no-trigger.json
+++ /dev/null
@@ -1,2152 +0,0 @@
-[
-  {
-    "cmd": [],
-    "name": "checkout pigweed"
-  },
-  {
-    "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\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: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nmatch_branch: true\nsubmodule_timeout_sec: 600\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.not matching branch names",
-    "~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,
-    "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,
-    "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,
-    "name": "checkout pigweed.cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "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",
-    "infra_step": true,
-    "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",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.set fetch.uriprotocols",
-    "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",
-    "infra_step": true,
-    "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",
-    "infra_step": true,
-    "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",
-    "infra_step": true,
-    "name": "checkout pigweed.cache.git submodule sync",
-    "timeout": 600.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",
-    "infra_step": true,
-    "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,
-    "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,
-    "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
-      }
-    },
-    "name": "checkout pigweed.git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "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",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "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",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main",
-      "--recurse-submodules"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "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",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "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",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git checkout.submodule",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "sync"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.submodule.git submodule sync",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "name": "checkout pigweed.git checkout.submodule.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@3@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed.git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/co",
-    "name": "checkout pigweed.git log.[START_DIR]/co",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-f",
-      "-d"
-    ],
-    "cwd": "[START_DIR]/co",
-    "name": "checkout pigweed.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/co",
-    "name": "checkout pigweed.git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "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,
-    "name": "checkout pigweed.mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/co",
-    "name": "checkout pigweed.submodule-status",
-    "timeout": 600.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,
-    "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",
-    "name": "checkout pigweed.log",
-    "timeout": 600.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,
-    "name": "checkout pigweed.write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2)",
-    "~followup_annotations": [
-      "@@@STEP_LINK@applied pigweed:1234 (.)@https://pigweed-review.googlesource.com/c/1234@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).options",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_SUMMARY_TEXT@remote: \"https://pigweed.googlesource.com/pigweed/pigweed\"\nbranch: \"main\"\nuse_trigger: true\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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\"\nrepo_init_timeout_sec: 20\nrepo_sync_timeout_sec: 120\nnumber_of_attempts: 3\nsubmodule_timeout_sec: 600\nuse_trigger: true\n@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "ls-remote",
-      "--heads",
-      "https://pigweed.googlesource.com/pigweed/pigweed",
-      "main"
-    ],
-    "name": "checkout pigweed (2).change data.git ls-remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@",
-      "@@@STEP_LOG_LINE@stdout@h3ll0\trefs/heads/main@@@",
-      "@@@STEP_LOG_END@stdout@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data.ensure infra/tools/luci/gerrit/${platform}",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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,
-    "name": "checkout pigweed (2).change data.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\": \"commit:h3ll0\"}}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).change data.number",
-    "timeout": 30,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@",
-      "@@@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 (2).change data.changes",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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=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)@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).not matching branch names",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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",
-      "copy",
-      "",
-      "[CACHE]/git/.GUARD_FILE"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).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,
-    "name": "checkout pigweed (2).cache.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).cache.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache.timeout 10s",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--prune",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "--no-recurse-submodules"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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",
-    "infra_step": true,
-    "name": "checkout pigweed (2).cache.git merge",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).cache.timeout 10s (2)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--recursive",
-      "--force",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[CACHE]/git/pigweed.googlesource.com-pigweed-pigweed",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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,
-    "name": "checkout pigweed (2).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]/project"
-    ],
-    "infra_step": true,
-    "name": "checkout pigweed (2).copy from cache",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git checkout",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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]/project"
-    ],
-    "infra_step": true,
-    "luci_context": {
-      "deadline": {
-        "grace_period": 30.0,
-        "soft_deadline": 1337000028.0
-      }
-    },
-    "name": "checkout pigweed (2).git checkout.makedirs",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "init"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git remote",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "core.longpaths",
-      "true"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.set core.longpaths",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "config",
-      "fetch.uriprotocols",
-      "https"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.set fetch.uriprotocols",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "fetch",
-      "--tags",
-      "--jobs",
-      "4",
-      "origin",
-      "main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git fetch",
-    "timeout": 1200.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-f",
-      "FETCH_HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git checkout",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "clean",
-      "-f",
-      "-d",
-      "-x"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).git checkout.git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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 (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git checkout patch",
-    "timeout": 600.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]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git remote add",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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/main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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/main"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.git set upstream",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.git rev-parse",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).apply pigweed:1234.post-rebase log",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).apply pigweed:1234.timeout 10s (3)",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "--detach"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.detach",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "update",
-      "--init",
-      "--recursive",
-      "--jobs",
-      "4"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.git submodule update",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "checkout",
-      "-"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).apply pigweed:1234.reattach",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "python3",
-      "RECIPE_MODULE[pigweed::checkout]/resources/submodule_status.py",
-      "[START_DIR]/project",
-      "/path/to/tmp/json",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).submodule status",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@{}@@@",
-      "@@@STEP_LOG_END@json.output@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]",
-      "[CLEANUP]/tmp_tmp_1"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "checkout pigweed (2).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\": \"main\", \"commit_message\": \"\", \"current_revision\": null, \"gerrit_name\": \"pigweed\", \"is_merge\": false, \"number\": 1234, \"patchset\": null, \"path\": \".\", \"project\": null, \"rebase\": false, \"ref\": \"h3ll0\", \"remote\": \"https://pigweed.googlesource.com/pigweed/pigweed\", \"submitted\": true, \"topic\": null}]@@@",
-      "@@@STEP_LOG_END@tmp_tmp_1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--oneline",
-      "-n",
-      "10"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git log.[START_DIR]/project",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@2@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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]/project",
-    "name": "checkout pigweed (2).git clean",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "status"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).git status",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "checkout pigweed (2).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=None, branch='main', 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=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,
-    "name": "checkout pigweed (2).mkdir",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "submodule",
-      "status",
-      "--recursive"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "checkout pigweed (2).submodule-status",
-    "timeout": 600.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,
-    "name": "checkout pigweed (2).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]/project",
-    "name": "checkout pigweed (2).log",
-    "timeout": 600.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,
-    "name": "checkout pigweed (2).write git log",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_END@git.log@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--max-count=1",
-      "--pretty=format:%H"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "get new revision",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@2222222222222222222222222222222222222222@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "[START_DIR]/co/WORKSPACE",
-      "/path/to/tmp/"
-    ],
-    "infra_step": true,
-    "name": "read old WORKSPACE",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"other-repo\"@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"invalid commit line won't be found\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Multiple@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: roll@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: comment@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: lines!@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"1111111111111111111111111111111111111111\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"missing final quote/comma so will miss this line@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_END@WORKSPACE@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "found other remote 'https://pigweed.googlesource.com/other/repo.git'",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@lines@2 @@@",
-      "@@@STEP_LOG_LINE@lines@3 @@@",
-      "@@@STEP_LOG_LINE@lines@4 @@@",
-      "@@@STEP_LOG_LINE@lines@5 @@@",
-      "@@@STEP_LOG_LINE@lines@6 @@@",
-      "@@@STEP_LOG_LINE@lines@7 @@@",
-      "@@@STEP_LOG_LINE@lines@8 @@@",
-      "@@@STEP_LOG_LINE@lines@9 @@@",
-      "@@@STEP_LOG_LINE@lines@10 git_repository(@@@",
-      "@@@STEP_LOG_LINE@lines@11     name = \"other-repo\"@@@",
-      "@@@STEP_LOG_LINE@lines@12     remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@lines@13     commit = \"invalid commit line won't be found\",@@@",
-      "@@@STEP_LOG_LINE@lines@14 )@@@",
-      "@@@STEP_LOG_LINE@lines@15 @@@",
-      "@@@STEP_LOG_LINE@lines@16 git_repository(@@@",
-      "@@@STEP_LOG_LINE@lines@17     module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@lines@18     # ROLL: Multiple@@@",
-      "@@@STEP_LOG_LINE@lines@19     # ROLL: roll@@@",
-      "@@@STEP_LOG_LINE@lines@20     # ROLL: comment@@@",
-      "@@@STEP_LOG_LINE@lines@21     # ROLL: lines!@@@",
-      "@@@STEP_LOG_LINE@lines@22     commit = \"1111111111111111111111111111111111111111\",@@@",
-      "@@@STEP_LOG_END@lines@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "found equivalent remote 'https://pigweed.googlesource.com/pigweed/pigweed.git'",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@lines@13     commit = \"invalid commit line won't be found\",@@@",
-      "@@@STEP_LOG_LINE@lines@14 )@@@",
-      "@@@STEP_LOG_LINE@lines@15 @@@",
-      "@@@STEP_LOG_LINE@lines@16 git_repository(@@@",
-      "@@@STEP_LOG_LINE@lines@17     module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@lines@18     # ROLL: Multiple@@@",
-      "@@@STEP_LOG_LINE@lines@19     # ROLL: roll@@@",
-      "@@@STEP_LOG_LINE@lines@20     # ROLL: comment@@@",
-      "@@@STEP_LOG_LINE@lines@21     # ROLL: lines!@@@",
-      "@@@STEP_LOG_LINE@lines@22     commit = \"1111111111111111111111111111111111111111\",@@@",
-      "@@@STEP_LOG_LINE@lines@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@lines@24     git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@lines@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@lines@26 )@@@",
-      "@@@STEP_LOG_LINE@lines@27 @@@",
-      "@@@STEP_LOG_LINE@lines@28 git_repository(@@@",
-      "@@@STEP_LOG_LINE@lines@29     name = \"missing final quote/comma so will miss this line@@@",
-      "@@@STEP_LOG_LINE@lines@30     remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@lines@31     commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@lines@32 )@@@",
-      "@@@STEP_LOG_LINE@lines@33 @@@",
-      "@@@STEP_LOG_END@lines@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "found other remote 'https://pigweed.googlesource.com/third/repo.git'",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@lines@20     # ROLL: comment@@@",
-      "@@@STEP_LOG_LINE@lines@21     # ROLL: lines!@@@",
-      "@@@STEP_LOG_LINE@lines@22     commit = \"1111111111111111111111111111111111111111\",@@@",
-      "@@@STEP_LOG_LINE@lines@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@lines@24     git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@lines@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@lines@26 )@@@",
-      "@@@STEP_LOG_LINE@lines@27 @@@",
-      "@@@STEP_LOG_LINE@lines@28 git_repository(@@@",
-      "@@@STEP_LOG_LINE@lines@29     name = \"missing final quote/comma so will miss this line@@@",
-      "@@@STEP_LOG_LINE@lines@30     remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@lines@31     commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@lines@32 )@@@",
-      "@@@STEP_LOG_LINE@lines@33 @@@",
-      "@@@STEP_LOG_LINE@lines@34 @@@",
-      "@@@STEP_LOG_LINE@lines@35 @@@",
-      "@@@STEP_LOG_LINE@lines@36 @@@",
-      "@@@STEP_LOG_LINE@lines@37 @@@",
-      "@@@STEP_LOG_LINE@lines@38 @@@",
-      "@@@STEP_LOG_LINE@lines@39 @@@",
-      "@@@STEP_LOG_LINE@lines@40 @@@",
-      "@@@STEP_LOG_END@lines@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "lines",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@0_center@23@@@",
-      "@@@STEP_LOG_END@0_center@@@",
-      "@@@STEP_LOG_LINE@1_orig@13     commit = \"invalid commit line won't be found\",@@@",
-      "@@@STEP_LOG_LINE@1_orig@14 )@@@",
-      "@@@STEP_LOG_LINE@1_orig@15 @@@",
-      "@@@STEP_LOG_LINE@1_orig@16 git_repository(@@@",
-      "@@@STEP_LOG_LINE@1_orig@17     module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@1_orig@18     # ROLL: Multiple@@@",
-      "@@@STEP_LOG_LINE@1_orig@19     # ROLL: roll@@@",
-      "@@@STEP_LOG_LINE@1_orig@20     # ROLL: comment@@@",
-      "@@@STEP_LOG_LINE@1_orig@21     # ROLL: lines!@@@",
-      "@@@STEP_LOG_LINE@1_orig@22     commit = \"1111111111111111111111111111111111111111\",@@@",
-      "@@@STEP_LOG_LINE@1_orig@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@1_orig@24     git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@1_orig@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@1_orig@26 )@@@",
-      "@@@STEP_LOG_LINE@1_orig@27 @@@",
-      "@@@STEP_LOG_LINE@1_orig@28 git_repository(@@@",
-      "@@@STEP_LOG_LINE@1_orig@29     name = \"missing final quote/comma so will miss this line@@@",
-      "@@@STEP_LOG_LINE@1_orig@30     remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@1_orig@31     commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@1_orig@32 )@@@",
-      "@@@STEP_LOG_LINE@1_orig@33 @@@",
-      "@@@STEP_LOG_END@1_orig@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@15 @@@",
-      "@@@STEP_LOG_LINE@2_trimmed@16 git_repository(@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@17     module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@18     # ROLL: Multiple@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@19     # ROLL: roll@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@20     # ROLL: comment@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@21     # ROLL: lines!@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@22     commit = \"1111111111111111111111111111111111111111\",@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@24     git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@2_trimmed@26 )@@@",
-      "@@@STEP_LOG_END@2_trimmed@@@",
-      "@@@STEP_LOG_LINE@3_sorted@23     remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@3_sorted@22     commit = \"1111111111111111111111111111111111111111\",@@@",
-      "@@@STEP_LOG_LINE@3_sorted@24     git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@3_sorted@21     # ROLL: lines!@@@",
-      "@@@STEP_LOG_LINE@3_sorted@25     strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@3_sorted@20     # ROLL: comment@@@",
-      "@@@STEP_LOG_LINE@3_sorted@26 )@@@",
-      "@@@STEP_LOG_LINE@3_sorted@19     # ROLL: roll@@@",
-      "@@@STEP_LOG_LINE@3_sorted@18     # ROLL: Multiple@@@",
-      "@@@STEP_LOG_LINE@3_sorted@17     module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@3_sorted@16 git_repository(@@@",
-      "@@@STEP_LOG_LINE@3_sorted@15 @@@",
-      "@@@STEP_LOG_END@3_sorted@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "vpython3",
-      "-u",
-      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
-      "--json-output",
-      "/path/to/tmp/json",
-      "copy",
-      "git_repository(\n    name = \"other-repo\"\n    remote = \"https://pigweed.googlesource.com/other/repo.git\",\n    commit = \"invalid commit line won't be found\",\n)\n\ngit_repository(\n    module_name = \"pigweed\",\n    # ROLL: Warning: this entry is automatically updated.\n    # ROLL: Last updated 2012-05-14.\n    # ROLL: By https://cr-buildbucket.appspot.com/build/0.\n    commit = \"2222222222222222222222222222222222222222\",\n    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",\n    git_repository_attribute_test = \"ignored\",\n    strip_prefix = \"pw_toolchain_bazel\",\n)\n\ngit_repository(\n    name = \"missing final quote/comma so will miss this line\n    remote = \"https://pigweed.googlesource.com/third/repo.git\",\n    commit = \"2222222222222222222222222222222222222222\",\n)\n",
-      "[START_DIR]/co/WORKSPACE"
-    ],
-    "infra_step": true,
-    "name": "write new WORKSPACE",
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"other-repo\"@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/other/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"invalid commit line won't be found\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    module_name = \"pigweed\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Warning: this entry is automatically updated.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: Last updated 2012-05-14.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    # ROLL: By https://cr-buildbucket.appspot.com/build/0.@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/pigweed/pigweed.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    git_repository_attribute_test = \"ignored\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    strip_prefix = \"pw_toolchain_bazel\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@git_repository(@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    name = \"missing final quote/comma so will miss this line@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    remote = \"https://pigweed.googlesource.com/third/repo.git\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@    commit = \"2222222222222222222222222222222222222222\",@@@",
-      "@@@STEP_LOG_LINE@WORKSPACE@)@@@",
-      "@@@STEP_LOG_END@WORKSPACE@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "get roll direction",
-    "~followup_annotations": [
-      "@@@STEP_SUMMARY_TEXT@forward@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "1111111111111111111111111111111111111111",
-      "2222222222222222222222222222222222222222"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "get roll direction.is forward",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "merge-base",
-      "--is-ancestor",
-      "2222222222222222222222222222222222222222",
-      "1111111111111111111111111111111111111111"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "get roll direction.is backward",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "remote"
-  },
-  {
-    "cmd": [
-      "git",
-      "remote"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "remote.name",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "remote",
-      "get-url",
-      "origin"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "remote.url",
-    "timeout": 600.0,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "pigweed"
-  },
-  {
-    "cmd": [
-      "git",
-      "log",
-      "--pretty=format:%H\n%an\n%ae\n%B",
-      "-z",
-      "1111111111111111111111111111111111111111..2222222222222222222222222222222222222222"
-    ],
-    "cwd": "[START_DIR]/project",
-    "name": "pigweed.git log",
-    "timeout": 600.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\": {\"q\": \"commit:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"}}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "pigweed.get change-id",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@[@@@",
-      "@@@STEP_LOG_LINE@json.output@  {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"_number\": 12345@@@",
-      "@@@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:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"@@@",
-      "@@@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-detail",
-      "-host",
-      "https://pigweed-review.googlesource.com",
-      "-input",
-      "{\"change_id\": \"12345\"}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/project",
-    "infra_step": true,
-    "name": "pigweed.get 12345",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"owner\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"email\": \"author@example.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"name\": \"author\"@@@",
-      "@@@STEP_LOG_LINE@json.output@  },@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"reviewers\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@    \"REVIEWER\": [@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"reviewer@example.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"reviewer\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"nobody@google.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"nobody\"@@@",
-      "@@@STEP_LOG_LINE@json.output@      },@@@",
-      "@@@STEP_LOG_LINE@json.output@      {@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"email\": \"robot@gserviceaccount.com\",@@@",
-      "@@@STEP_LOG_LINE@json.output@        \"name\": \"robot\"@@@",
-      "@@@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@@@",
-      "@@@STEP_LOG_LINE@json.input@{@@@",
-      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"12345\"@@@",
-      "@@@STEP_LOG_LINE@json.input@}@@@",
-      "@@@STEP_LOG_END@json.input@@@",
-      "@@@STEP_LINK@gerrit link@https://pigweed-review.googlesource.com/q/12345@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "roll message"
-  },
-  {
-    "cmd": [],
-    "name": "roll message.message for pigweed",
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@template@roll: {project_name}: {sanitized_message}@@@",
-      "@@@STEP_LOG_LINE@template@@@@",
-      "@@@STEP_LOG_LINE@template@{remote}@@@",
-      "@@@STEP_LOG_LINE@template@{project_name} Rolled-Commits: {old_revision:.15}..{new_revision:.15}@@@",
-      "@@@STEP_LOG_END@template@@@",
-      "@@@STEP_LOG_LINE@kwargs@'new_revision': '2222222222222222222222222222222222222222'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'old_revision': '1111111111111111111111111111111111111111'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'original_message': 'foo\\nbar\\n\\nChange-Id: I1111'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'project_name': 'pigweed'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'remote': 'https://pigweed.googlesource.com/pigweed/pigweed'@@@",
-      "@@@STEP_LOG_LINE@kwargs@'sanitized_message': 'foo\\nbar\\n'@@@",
-      "@@@STEP_LOG_END@kwargs@@@",
-      "@@@STEP_LOG_LINE@message@roll: pigweed: foo@@@",
-      "@@@STEP_LOG_LINE@message@bar@@@",
-      "@@@STEP_LOG_LINE@message@@@@",
-      "@@@STEP_LOG_LINE@message@@@@",
-      "@@@STEP_LOG_LINE@message@https://pigweed.googlesource.com/pigweed/pigweed@@@",
-      "@@@STEP_LOG_LINE@message@pigweed Rolled-Commits: 111111111111111..222222222222222@@@",
-      "@@@STEP_LOG_END@message@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "ls-files",
-      "--modified",
-      "--deleted",
-      "--exclude-standard"
-    ],
-    "cwd": "[START_DIR]/co",
-    "name": "check for no-op commit",
-    "timeout": 300.0,
-    "~followup_annotations": [
-      "@@@STEP_LOG_LINE@stdout@hello@@@",
-      "@@@STEP_LOG_END@stdout@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "add",
-      "--update"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "name": "git add",
-    "timeout": 300.0
-  },
-  {
-    "cmd": [
-      "git",
-      "rev-parse",
-      "HEAD"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "name": "git rev-parse",
-    "timeout": 300.0
-  },
-  {
-    "cmd": [],
-    "name": "calculate Change-Id",
-    "~followup_annotations": [
-      "@@@STEP_TEXT@Ic42e2adc21e51877c21ce043876310f40a23ba91@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "diff",
-      "--unified=0",
-      "--cached"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "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################"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "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\": [\"CURRENT_COMMIT\", \"CURRENT_REVISION\", \"MESSAGES\", \"DETAILED_ACCOUNTS\"], \"q\": \"change:pigweed/pigweed~main~Ic42e2adc21e51877c21ce043876310f40a23ba91\"}}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "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@      \"CURRENT_COMMIT\",@@@",
-      "@@@STEP_LOG_LINE@json.input@      \"CURRENT_REVISION\",@@@",
-      "@@@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~Ic42e2adc21e51877c21ce043876310f40a23ba91\"@@@",
-      "@@@STEP_LOG_LINE@json.input@  }@@@",
-      "@@@STEP_LOG_LINE@json.input@}@@@",
-      "@@@STEP_LOG_END@json.input@@@"
-    ]
-  },
-  {
-    "cmd": [
-      "git",
-      "commit",
-      "-m",
-      "roll: pigweed: foo\nbar\n\n\nhttps://pigweed.googlesource.com/pigweed/pigweed\npigweed Rolled-Commits: 111111111111111..222222222222222\nRoller-URL: https://ci.chromium.org/b/0\nCQ-Do-Not-Cancel-Tryjobs: true\nChange-Id: Ic42e2adc21e51877c21ce043876310f40a23ba91",
-      "-a",
-      "--no-verify",
-      "--author",
-      "author <author@pigweed.infra.roller.example.com>"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "name": "git commit",
-    "timeout": 600.0
-  },
-  {
-    "cmd": [
-      "git",
-      "push",
-      "--push-option",
-      "nokeycheck",
-      "origin",
-      "HEAD:refs/for/main%l=Commit-Queue+2"
-    ],
-    "cwd": "[START_DIR]/co",
-    "infra_step": true,
-    "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~Ic42e2adc21e51877c21ce043876310f40a23ba91@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "output gerrit change id",
-    "~followup_annotations": [
-      "@@@SET_BUILD_PROPERTY@gerrit_changes@[{\"change_id\": \"pigweed/pigweed~main~Ic42e2adc21e51877c21ce043876310f40a23ba91\", \"host\": \"pigweed-review.googlesource.com\"}]@@@"
-    ]
-  },
-  {
-    "cmd": [],
-    "name": "check for completion"
-  },
-  {
-    "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~Ic42e2adc21e51877c21ce043876310f40a23ba91\", \"params\": {\"o\": [\"CURRENT_REVISION\", \"DETAILED_ACCOUNTS\"]}}",
-      "-output",
-      "/path/to/tmp/json"
-    ],
-    "infra_step": true,
-    "name": "check for completion.check if done (0)",
-    "timeout": 600,
-    "~followup_annotations": [
-      "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"current_revision\": \"abc123\",@@@",
-      "@@@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~Ic42e2adc21e51877c21ce043876310f40a23ba91\",@@@",
-      "@@@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@    ]@@@",
-      "@@@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~Ic42e2adc21e51877c21ce043876310f40a23ba91@@@"
-    ]
-  },
-  {
-    "name": "$result",
-    "summaryMarkdown": "Roll succeeded. [gerrit link](https://pigweed-review.googlesource.com/q/pigweed/pigweed~main~Ic42e2adc21e51877c21ce043876310f40a23ba91)"
-  }
-]
\ No newline at end of file
diff --git a/recipes/bazel_roller.proto b/recipes/bazel_roller.proto
index 5b16e08..4b365d6 100644
--- a/recipes/bazel_roller.proto
+++ b/recipes/bazel_roller.proto
@@ -17,31 +17,39 @@
 package recipes.pigweed.bazel_roller;
 
 import "recipe_modules/fuchsia/auto_roller/options.proto";
+import "recipe_modules/pigweed/bazel_roll/git_repository.proto";
 import "recipe_modules/pigweed/checkout/options.proto";
 
 message InputProperties {
   // Checkout module options.
   recipe_modules.pigweed.checkout.Options checkout_options = 1;
 
+  // Git repositories to update.
+  repeated recipe_modules.pigweed.bazel_roll.GitRepository git_repositories = 2;
+
   // Auto roller module options.
-  recipe_modules.fuchsia.auto_roller.Options auto_roller_options = 2;
+  recipe_modules.fuchsia.auto_roller.Options auto_roller_options = 3;
 
   // The path of the WORKSPACE file to update. If not provided the recipe will
   // look for a top-level WORKSPACE or MODULE.bazel file. If provided and is a
   // directory, looks for those files in that directory.
-  string workspace_path = 3;
+  // DEPRECATED.
+  string workspace_path = 4;
 
   // Name for project to be rolled. By default will extract from WORKSPACE.
-  string project_name = 4;
+  string project_name = 5;
+  // DEPRECATED.
 
   // Repository referred to by the WORKSPACE file.
-  string project_remote = 5;
+  // DEPRECATED.
+  string project_remote = 6;
 
   // Branch to get latest from when new_revision is None and no buildbucket
   // trigger. Default: "main".
-  string project_branch = 6;
+  // DEPRECATED.
+  string project_branch = 7;
 
   // Forge the author so rolls of single commits are attributed to the original
   // commit author.
-  bool forge_author = 7;
+  bool forge_author = 8;
 }
diff --git a/recipes/bazel_roller.py b/recipes/bazel_roller.py
index c4d6ed8..3a1ed91 100644
--- a/recipes/bazel_roller.py
+++ b/recipes/bazel_roller.py
@@ -39,10 +39,8 @@
 
 import attrs
 from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
+from PB.recipe_modules.pigweed.bazel_roll.git_repository import GitRepository
 from PB.recipes.pigweed.bazel_roller import InputProperties
-from PB.recipe_modules.pigweed.checkout.options import (
-    Options as CheckoutOptions,
-)
 from recipe_engine import post_process
 
 if TYPE_CHECKING:  # pragma: no cover
@@ -52,182 +50,15 @@
 
 DEPS = [
     'fuchsia/auto_roller',
-    'pigweed/bazel',
+    'pigweed/bazel_roll',
     'pigweed/checkout',
     'pigweed/roll_util',
-    'recipe_engine/buildbucket',
-    'recipe_engine/path',
     'recipe_engine/properties',
-    'recipe_engine/step',
 ]
 
 PROPERTIES = InputProperties
 
 
-def _workspace_path(
-    api: recipe_api.RecipeScriptApi,
-    root: config_types.Path,
-    value: str,
-) -> config_types.Path:
-    """Figure out the location of the WORKSPACE or MODULE.bazel file.
-
-    If value is '', look for root / 'WORKSPACE' and root / 'MODULE.bazel'. If
-    exactly one exists, return it. If not, error out.
-
-    If root / value is a file, return it.
-
-    If root / value is a directory, set root = root / value and apply the above
-    logic. This enables applying this logic to subdirectories.
-
-    Args:
-        api: Recipe API object.
-        root: Checkout root.
-        value: Relative path specified in properties.
-
-    Returns:
-        Path to the WORKSPACE or MODULE.bazel file.
-    """
-    if value:
-        value_path = root / value
-        api.path.mock_add_file(value_path)
-        if api.path.isfile(value_path):
-            return value_path
-        elif api.path.isdir(value_path):  # pragma: no cover
-            root = value_path
-        else:
-            api.step.empty(  # pragma: no cover
-                f'{value_path} does not exist',
-                status='FAILURE',
-            )
-
-    workspace = root / 'WORKSPACE'
-    module_bazel = root / 'MODULE.bazel'
-
-    api.path.mock_add_file(workspace)
-
-    if api.path.isfile(module_bazel) and api.path.isfile(workspace):
-        api.step.empty(  # pragma: no cover
-            f'{module_bazel} and {workspace} both exist',
-            status='FAILURE',
-        )
-
-    if api.path.isfile(module_bazel):
-        return module_bazel  # pragma: no cover
-
-    if api.path.isfile(workspace):
-        return workspace
-
-    api.step.empty(  # pragma: no cover
-        'no WORKSPACE or MODULE.bazel file found',
-        status='FAILURE',
-    )
-
-
-def _update_git_repository(
-    api: recipe_api.RecipeScriptApi,
-    checkout: checkout_api.CheckoutContext,
-    workspace: str | None,
-    project_name: str | None,
-    project_remote: str,
-    project_branch: str,
-) -> dict[str, api.roll_util.Roll]:
-    workspace_path = _workspace_path(api, checkout.root, workspace)
-
-    new_revision: Optional[str] = None
-
-    # First, try to get new_revision from the trigger.
-    bb_remote: Optional[str] = None
-    commit: common_pb2.GitilesCommit = (
-        api.buildbucket.build.input.gitiles_commit
-    )
-    if commit and commit.project:
-        new_revision = commit.id
-        host: str = commit.host
-        bb_remote: str = f'https://{host}/{commit.project}'
-
-    # If we still don't have a revision then it wasn't in the trigger. (Perhaps
-    # this was manually triggered.) In this case we update to the
-    # property-specified branch HEAD.
-    if new_revision is None:
-        new_revision = project_branch
-
-    # If this was triggered by a gitiles poller, check that the triggering
-    # repository matches project_remote. Exception: allow a trigger to be for
-    # the top-level project instead.
-    use_trigger_for_project = True
-
-    if bb_remote:
-        if checkout.remotes_equivalent(checkout.options.remote, bb_remote):
-            use_trigger_for_project = False
-
-        elif not checkout.remotes_equivalent(project_remote, bb_remote):
-            api.step.empty(
-                'triggering repository ({}) does not match project remote '
-                '({})'.format(bb_remote, project_remote),
-                status='FAILURE',
-            )
-
-    project_dir = api.path.start_dir / 'project'
-
-    project_checkout = api.checkout(
-        CheckoutOptions(
-            remote=project_remote,
-            branch=project_branch,
-            use_trigger=use_trigger_for_project,
-        ),
-        root=project_dir,
-    )
-
-    # In case new_revision is a branch name we need to retrieve the hash it
-    # resolves to.
-    if not re.search(r'^[0-9a-f]{40}$', new_revision):
-        new_revision = api.checkout.get_revision(
-            project_dir, 'get new revision', test_data='2' * 40
-        )
-
-    full_workspace_path = checkout.root / workspace_path
-
-    update_result = api.bazel.update_commit_hash(
-        checkout=checkout,
-        project_remote=project_remote,
-        new_revision=new_revision,
-        path=full_workspace_path,
-    )
-    if not update_result:
-        api.step.empty('failed to update commit hash', status='FAILURE')
-
-    direction: api.roll_util.Direction = api.roll_util.get_roll_direction(
-        project_dir, update_result.old_revision, new_revision
-    )
-
-    # If the primary roll is not necessary or is backwards we can exit
-    # immediately.
-    if not api.roll_util.can_roll(direction):
-        api.roll_util.skip_roll_step(
-            project_remote, update_result.old_revision, new_revision
-        )
-        return
-
-    project_name = project_name or update_result.project_name
-    if not project_name:
-        api.step.empty(
-            f'could not find name line in {full_workspace_path}',
-            status='FAILURE',
-        )
-
-    rolls: dict[str, api.roll_util.Roll] = {
-        workspace_path: api.roll_util.create_roll(
-            project_name=project_name,
-            old_revision=update_result.old_revision,
-            new_revision=new_revision,
-            proj_dir=project_dir,
-            direction=direction,
-        ),
-    }
-
-    return rolls
-
-
 def RunSteps(  # pylint: disable=invalid-name
     api: recipe_api.RecipeScriptApi,
     props: InputProperties,
@@ -240,16 +71,24 @@
         props.checkout_options
     )
 
-    rolls = _update_git_repository(
-        api,
+    if not props.git_repositories:
+        props.git_repositories.append(
+            GitRepository(
+                workspace_path=props.workspace_path,
+                name=props.project_name,
+                remote=props.project_remote,
+                branch=props.project_branch,
+            )
+        )
+
+    assert len(props.git_repositories) == 1
+
+    rolls = api.bazel_roll.update_git_repository(
         checkout=checkout,
-        workspace=props.workspace_path,
-        project_name=props.project_name,
-        project_remote=props.project_remote,
-        project_branch=props.project_branch or 'main',
+        git_repository=props.git_repositories[0],
     )
     if not rolls:
-        return
+        return  # pragma: no cover
 
     authors: Sequence[api.roll_util.Account] = api.roll_util.authors(
         *rolls.values()
@@ -306,43 +145,3 @@
         commit_data('pigweed', prefix=''),
         api.auto_roller.success(),
     )
-
-    yield api.test(
-        'unrecognized-remote',
-        properties(project_remote=_url('unrecognized-remote')),
-        api.roll_util.properties(commit_divider='--divider--'),
-        trigger('unrecognized-remote'),
-        api.post_process(post_process.MustRun, 'failed to update commit hash'),
-        status='FAILURE',
-    )
-
-    yield api.test(
-        'bad-trigger',
-        properties(project_remote=_url('foo'), workspace_path='bar/WORKSPACE'),
-        trigger('bar'),
-        status='FAILURE',
-    )
-
-    yield api.test(
-        'name-not-found',
-        properties(project_remote=_url('third/repo')),
-        trigger('third/repo'),
-        api.roll_util.forward_roll(),
-        api.post_process(post_process.MustRunRE, r'could not find name.*'),
-        api.post_process(post_process.DropExpectation),
-        status='FAILURE',
-    )
-
-    yield api.test(
-        'no-trigger',
-        properties(project_remote=_url('pigweed/pigweed')),
-        api.roll_util.forward_roll(),
-        commit_data('pigweed', prefix=''),
-        api.auto_roller.success(),
-    )
-
-    yield api.test(
-        'backwards',
-        properties(project_remote=_url('pigweed/pigweed')),
-        api.roll_util.backward_roll(),
-    )