Add support for subbuild tests.

This CL adds support for test configs and executing them as subbuilds.

Change-Id: If9ecaa6dbfd5e0c00c3be50ed192a2c6d8fc662f
Bug: https://github.com/flutter/flutter/issues/81855
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/17540
Reviewed-by: Keyong Han <keyonghan@google.com>
Commit-Queue: Godofredo Contreras <godofredoc@google.com>
diff --git a/recipe_modules/shard_util_v2/api.py b/recipe_modules/shard_util_v2/api.py
index 03daedc..91df744 100644
--- a/recipe_modules/shard_util_v2/api.py
+++ b/recipe_modules/shard_util_v2/api.py
@@ -54,18 +54,81 @@
         result[k] = v
     return result
 
+  def struct_to_dict(self, struct):
+    """Transforms a proto structure to a dictionary.
 
-  def schedule(self, builds, presentation):
-    """Schedules one subbuild per build."""
+    Args:
+      struct: A proto structure.
+    Returns:
+      A dictionary representation of the proto structure.
+
+    This is because the proto structures can not be passed to the BuildBucket or led
+    requests.
+    """
+    return {k: v for k, v in struct.items()}
+
+  def schedule_builds(self, builds, presentation):
+    """Schedule builds using the builds configurations.
+
+    Args:
+      builds(dict): The build configurations to be passed to BuildBucket or led.
+      presentation(StepPresentation): The step object used to add links and/or logs.
+    Returns:
+      A dictionary with a long build_id as key and SubbuildResult as value.
+    """
+    return self.schedule(builds, 'engine_v2/builder', presentation)
+
+  def schedule_tests(self, tests, build_results, presentation):
+    """Schedule tests using build_results for dependencies.
+
+    Args:
+      tests(dict): The test configurations to be passed to BuildBucket or led.
+      build_results: A dictionary with a long build_id as key and SubbuildResult as value.
+      presentation(StepPresentation): The step object used to add links and/or logs.
+    Returns:
+      A dictionary with a long build_id as key and SubbuildResult as value.
+    """
+    # Expand tests with result archives for dependencies.
+    results_map = {b.build_name: b for k, b in build_results.items()}
+    # build_results to map of builder name
+    updated_tests = []
+    for t in tests:
+      test = self.unfreeze_dict(t)
+      test['resolved_deps'] = []
+      for dep in test.get('dependencies', []):
+        dep_dict = self.struct_to_dict(
+            results_map[dep].build_proto.output.properties['cas_output_hash']
+        )
+        test['resolved_deps'].append(dep_dict)
+      updated_tests.append(test)
+    return self.schedule(updated_tests, 'engine_v2/tester', presentation)
+
+  def schedule(self, builds, recipe_name, presentation):
+    """Schedules one subbuild per build configuration.
+
+    Args:
+      builds(dict): The build/test configurations to be passed to BuildBucket or led.
+      recipe_name(str): A string with the recipe name to use.
+      presentation(StepPresentation): The step object used to add links and/or logs.
+    Returns:
+      A dictionary with a long build_id as key and SubbuildResult as value.
+    """
     build_list = [self.unfreeze_dict(b) for b in builds]
     if self.m.led.launched_by_led:
-      builds = self._schedule_with_led(build_list)
+      builds = self._schedule_with_led(build_list, recipe_name)
     else:
-      builds = self._schedule_with_bb(build_list)
+      builds = self._schedule_with_bb(build_list, recipe_name)
     return builds
 
-  def _schedule_with_led(self, builds):
-    """Schedules one subbuild per build."""
+  def _schedule_with_led(self, builds, recipe_name):
+    """Schedules one subbuild per build using led.
+
+    Args:
+      builds(dict): The build/test configurations to be passed to BuildBucket or led.
+      recipe_name(str): A string with the recipe name to use.
+    Returns:
+      A dictionary with a long build_id as key and SubbuildResult as value.
+    """
     # Dependencies get here as a frozen dict we need to force them back
     # to list of dicts.
     results = {}
@@ -87,7 +150,7 @@
         task_dimensions.append(common_pb2.RequestedDimension(key=k, value=v))
 
       # Override recipe.
-      drone_properties['recipe'] = 'engine_v2/builder'
+      drone_properties['recipe'] = recipe_name
 
       if self.m.led.launched_by_led:
         # If coming from led Launch sub-build using led.
@@ -108,7 +171,7 @@
         led_data.result.buildbucket.bbagent_args.build.infra.swarming.priority -= 20
         led_data = led_data.then("edit", *edit_args)
         led_data = led_data.then("edit", "-name", task_name)
-        led_data = led_data.then("edit", "-r", 'engine_v2/builder')
+        led_data = led_data.then("edit", "-r", recipe_name)
         for d in drone_dimensions:
           led_data = led_data.then("edit", "-d", d)
         led_data = self.m.led.inject_input_recipes(led_data)
@@ -119,12 +182,22 @@
             launch_res.launch_result.swarming_hostname,
         )
         results[task_name] = SubbuildResult(
-            builder=task_name, build_id=task_id, url=build_url, build_name=task_name
+            builder=task_name,
+            build_id=task_id,
+            url=build_url,
+            build_name=task_name
         )
     return results
 
-  def _schedule_with_bb(self, builds):
-    """Schedules builds using builbbucket."""
+  def _schedule_with_bb(self, builds, recipe_name):
+    """Schedules builds using builbbucket.
+
+    Args:
+      builds(dict): The build/test configurations to be passed to BuildBucket or led.
+      recipe_name(str): A string with the recipe name to use.
+    Returns:
+      A dictionary with a long build_id as key and SubbuildResult as value.
+    """
     swarming_parent_run_id = self.m.swarming.task_id
     reqs = []
     task_names = []
@@ -144,7 +217,7 @@
         k, v = d.split('=')
         task_dimensions.append(common_pb2.RequestedDimension(key=k, value=v))
       # Override recipe.
-      drone_properties['recipe'] = 'engine_v2/builder'
+      drone_properties['recipe'] = recipe_name
       properties = {
           key: val
           for key, val in drone_properties.items()
@@ -172,8 +245,10 @@
     for build, task_name in zip(scheduled_builds, task_names):
       build_url = "https://ci.chromium.org/b/%s" % build.id
       results[build.id] = SubbuildResult(
-          builder=build.builder.builder, build_id=build.id,
-          url=build_url, build_name=task_name
+          builder=build.builder.builder,
+          build_id=build.id,
+          url=build_url,
+          build_name=task_name
       )
     return results
 
@@ -240,11 +315,12 @@
     """
     build_ids = [build.build_id for build in tasks.values()]
     build_id_to_name = {
-        int(build.build_id): build.build_name
-        for build in tasks.values()
+        int(build.build_id): build.build_name for build in tasks.values()
     }
     bb_fields = self.m.buildbucket.DEFAULT_FIELDS.union({
-        "infra.swarming.task_id", "summary_markdown", "input",
+        "infra.swarming.task_id",
+        "summary_markdown",
+        "input",
     })
     # As of 2019-11-18, timeout defaults to something too short.
     # We never want this step to time out. We'd rather the whole build time out.
@@ -292,7 +368,9 @@
       )
     for build_id, build in builds.iteritems():
       builds[build_id] = SubbuildResult(
-          builder=build.builder.builder, build_id=build_id,
-          build_proto=build, build_name=build_id_to_name[int(build_id)]
+          builder=build.builder.builder,
+          build_id=build_id,
+          build_proto=build,
+          build_name=build_id_to_name[int(build_id)]
       )
     return builds
diff --git a/recipe_modules/shard_util_v2/examples/full.expected/presubmit_bb.json b/recipe_modules/shard_util_v2/examples/full.expected/presubmit_bb.json
index 01697ae..d68d39d 100644
--- a/recipe_modules/shard_util_v2/examples/full.expected/presubmit_bb.json
+++ b/recipe_modules/shard_util_v2/examples/full.expected/presubmit_bb.json
@@ -12,7 +12,7 @@
     ],
     "infra_step": true,
     "name": "launch builds.schedule",
-    "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"try\", \"builder\": \"Linux Staging Engine Drone\", \"project\": \"fuchsia\"}, \"dimensions\": [{\"key\": \"dimension1\", \"value\": \"abc\"}], \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gerritChanges\": [{\"change\": \"123456\", \"host\": \"fuchsia-review.googlesource.com\", \"patchset\": \"7\", \"project\": \"fuchsia\"}], \"priority\": 25, \"properties\": {\"build\": {\"drone_dimensions\": [\"dimension1=abc\"], \"generators\": [{\"name\": \"generator1\", \"script\": \"script1.sh\"}], \"gn\": [\"--ios\"], \"name\": \"ios_debug\", \"ninja\": {\"config\": \"ios_debug\", \"targets\": []}}, \"builds\": [{\"drone_dimensions\": [\"dimension1=abc\"], \"generators\": [{\"name\": \"generator1\", \"script\": \"script1.sh\"}], \"gn\": [\"--ios\"], \"name\": \"ios_debug\", \"ninja\": {\"config\": \"ios_debug\", \"targets\": []}}], \"dependencies\": [{\"dependency\": \"android_sdk\"}, {\"dependency\": \"chrome_and_driver\"}], \"environment\": \"Staging\", \"git_ref\": \"refs/123/master\", \"git_url\": \"http://abc\", \"recipe\": \"engine_v2/builder\", \"task_name\": \"mytask\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"swarming\": {\"parentRunId\": \"fake-task-id\"}, \"tags\": [{\"key\": \"cq_experimental\", \"value\": \"false\"}, {\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+    "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"try\", \"builder\": \"Linux Staging Engine Drone\", \"project\": \"fuchsia\"}, \"dimensions\": [{\"key\": \"dimension1\", \"value\": \"abc\"}], \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gerritChanges\": [{\"change\": \"123456\", \"host\": \"fuchsia-review.googlesource.com\", \"patchset\": \"7\", \"project\": \"fuchsia\"}], \"priority\": 25, \"properties\": {\"build\": {\"drone_dimensions\": [\"dimension1=abc\"], \"generators\": [{\"name\": \"generator1\", \"script\": \"script1.sh\"}], \"gn\": [\"--ios\"], \"name\": \"ios_debug\", \"ninja\": {\"config\": \"ios_debug\", \"targets\": []}}, \"builds\": [{\"drone_dimensions\": [\"dimension1=abc\"], \"generators\": [{\"name\": \"generator1\", \"script\": \"script1.sh\"}], \"gn\": [\"--ios\"], \"name\": \"ios_debug\", \"ninja\": {\"config\": \"ios_debug\", \"targets\": []}}], \"dependencies\": [{\"dependency\": \"android_sdk\"}, {\"dependency\": \"chrome_and_driver\"}], \"environment\": \"Staging\", \"git_ref\": \"refs/123/master\", \"git_url\": \"http://abc\", \"recipe\": \"engine_v2/builder\", \"task_name\": \"mytask\", \"tests\": [{\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"scripts\": [\"out/script.sh\"]}]}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"swarming\": {\"parentRunId\": \"fake-task-id\"}, \"tags\": [{\"key\": \"cq_experimental\", \"value\": \"false\"}, {\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
@@ -109,7 +109,21 @@
       "@@@STEP_LOG_LINE@request@          \"git_ref\": \"refs/123/master\", @@@",
       "@@@STEP_LOG_LINE@request@          \"git_url\": \"http://abc\", @@@",
       "@@@STEP_LOG_LINE@request@          \"recipe\": \"engine_v2/builder\", @@@",
-      "@@@STEP_LOG_LINE@request@          \"task_name\": \"mytask\"@@@",
+      "@@@STEP_LOG_LINE@request@          \"task_name\": \"mytask\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@request@            {@@@",
+      "@@@STEP_LOG_LINE@request@              \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@request@                \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@request@              ], @@@",
+      "@@@STEP_LOG_LINE@request@              \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@request@              \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@request@                \"test\"@@@",
+      "@@@STEP_LOG_LINE@request@              ], @@@",
+      "@@@STEP_LOG_LINE@request@              \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@request@                \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@request@              ]@@@",
+      "@@@STEP_LOG_LINE@request@            }@@@",
+      "@@@STEP_LOG_LINE@request@          ]@@@",
       "@@@STEP_LOG_LINE@request@        }, @@@",
       "@@@STEP_LOG_LINE@request@        \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", @@@",
       "@@@STEP_LOG_LINE@request@        \"swarming\": {@@@",
@@ -209,7 +223,10 @@
       "@@@STEP_LOG_LINE@json.output@        }, @@@",
       "@@@STEP_LOG_LINE@json.output@        \"output\": {@@@",
       "@@@STEP_LOG_LINE@json.output@          \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@json.output@            \"test_orchestration_inputs_hash\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@json.output@            \"cas_output_hash\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@              \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@json.output@              \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@json.output@            }@@@",
       "@@@STEP_LOG_LINE@json.output@          }@@@",
       "@@@STEP_LOG_LINE@json.output@        }, @@@",
       "@@@STEP_LOG_LINE@json.output@        \"status\": \"FAILURE\", @@@",
diff --git a/recipe_modules/shard_util_v2/examples/full.expected/presubmit_led.json b/recipe_modules/shard_util_v2/examples/full.expected/presubmit_led.json
index 33643ab..25246dc 100644
--- a/recipe_modules/shard_util_v2/examples/full.expected/presubmit_led.json
+++ b/recipe_modules/shard_util_v2/examples/full.expected/presubmit_led.json
@@ -33,6 +33,8 @@
       "led",
       "edit",
       "-p",
+      "tests=[{\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"scripts\": [\"out/script.sh\"]}]",
+      "-p",
       "$recipe_engine/led={\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \"rbe_cas_input\": {\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \"digest\": {\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \"size_bytes\": 91}}}",
       "-p",
       "recipe=\"engine_v2/builder\"",
@@ -146,7 +148,21 @@
       "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/builder\", @@@",
-      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
       "@@@STEP_LOG_LINE@proto.output@          }@@@",
       "@@@STEP_LOG_LINE@proto.output@        }@@@",
       "@@@STEP_LOG_LINE@proto.output@      }@@@",
@@ -164,7 +180,7 @@
       "ios_debug"
     ],
     "name": "launch builds.led edit (2)",
-    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\"\n}\n}\n}\n}\n}\n}",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}\n}\n}",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@proto.output@{@@@",
@@ -258,7 +274,21 @@
       "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/builder\", @@@",
-      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
       "@@@STEP_LOG_LINE@proto.output@          }@@@",
       "@@@STEP_LOG_LINE@proto.output@        }@@@",
       "@@@STEP_LOG_LINE@proto.output@      }@@@",
@@ -277,7 +307,7 @@
       "engine_v2/builder"
     ],
     "name": "launch builds.led edit (3)",
-    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\"\n}\n}\n}\n}, \n\"name\": \"ios_debug\"\n}\n}",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}, \n\"name\": \"ios_debug\"\n}\n}",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@proto.output@{@@@",
@@ -371,7 +401,21 @@
       "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/builder\", @@@",
-      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
       "@@@STEP_LOG_LINE@proto.output@          }@@@",
       "@@@STEP_LOG_LINE@proto.output@        }@@@",
       "@@@STEP_LOG_LINE@proto.output@      }@@@",
@@ -390,7 +434,7 @@
       "dimension1=abc"
     ],
     "name": "launch builds.led edit (4)",
-    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\"\n}\n}\n}\n}, \n\"name\": \"ios_debug\"\n}\n}",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}, \n\"name\": \"ios_debug\"\n}\n}",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@proto.output@{@@@",
@@ -484,7 +528,21 @@
       "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/builder\", @@@",
-      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
       "@@@STEP_LOG_LINE@proto.output@          }@@@",
       "@@@STEP_LOG_LINE@proto.output@        }@@@",
       "@@@STEP_LOG_LINE@proto.output@      }@@@",
@@ -503,7 +561,7 @@
       "146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e/91"
     ],
     "name": "launch builds.led edit (5)",
-    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\"\n}\n}\n}\n}, \n\"name\": \"ios_debug\"\n}\n}",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}, \n\"name\": \"ios_debug\"\n}\n}",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@proto.output@{@@@",
@@ -597,7 +655,21 @@
       "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
       "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/builder\", @@@",
-      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"ios_debug\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
       "@@@STEP_LOG_LINE@proto.output@          }@@@",
       "@@@STEP_LOG_LINE@proto.output@        }@@@",
       "@@@STEP_LOG_LINE@proto.output@      }@@@",
@@ -621,7 +693,7 @@
       "-modernize"
     ],
     "name": "launch builds.led launch",
-    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\"\n}\n}\n}\n}, \n\"name\": \"ios_debug\"\n}, \n\"cas_user_payload\": {\n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": \"91\"\n}\n}\n}",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"drone_dimensions\": [\n\"dimension1=abc\"\n], \n\"gn\": [], \n\"name\": \"ios_debug\", \n\"ninja\": [\n\"ios_debug\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/builder\", \n\"task_name\": \"ios_debug\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}, \n\"name\": \"ios_debug\"\n}, \n\"cas_user_payload\": {\n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": \"91\"\n}\n}\n}",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
@@ -638,51 +710,6 @@
     "cmd": [],
     "name": "collect builds",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@{@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"status\": \"SUCCESS\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"tags\": [@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"value\": \"false\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"key\": \"cq_experimental\"@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  ], @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"builder\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"project\": \"fuchsia\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"builder\": \"builder-subbuild1\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"bucket\": \"try\"@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  }, @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"createTime\": \"2018-05-25T23:50:17Z\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"output\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"test_orchestration_inputs_hash\": \"abc\"@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  }, @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"input\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"task_name\": \"mytask\"@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }, @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"gerritChanges\": [@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"project\": \"fuchsia\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"host\": \"chromium-review.googlesource.com\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"patchset\": \"7\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"change\": \"123456\"@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      }@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    ]@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  }, @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"infra\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"swarming\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"priority\": 30, @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"taskId\": \"abc123\"@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }, @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"resultdb\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"invocation\": \"invocations/build:8945511751514863186\"@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  }, @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"id\": \"8945511751514863186\"@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@}@@@",
-      "@@@STEP_LOG_END@fake-task-id-1-build.proto.json@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@{@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"status\": \"SUCCESS\", @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"tags\": [@@@",
@@ -693,17 +720,23 @@
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  ], @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"builder\": {@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    \"project\": \"fuchsia\", @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    \"builder\": \"builder-subbuild2\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    \"builder\": \"ios_debug\", @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    \"bucket\": \"try\"@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  }, @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"createTime\": \"2018-05-25T23:50:17Z\", @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"output\": {@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@      \"test_orchestration_inputs_hash\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@      \"cas_output_hash\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@        \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@        \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@      }@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    }@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  }, @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@      \"task_name\": \"mytask\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    }, @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    \"gerritChanges\": [@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@      {@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@        \"project\": \"fuchsia\", @@@",
@@ -719,12 +752,57 @@
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@      \"taskId\": \"abc123\"@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    }, @@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    \"resultdb\": {@@@",
-      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@      \"invocation\": \"invocations/build:8945511751514863187\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@      \"invocation\": \"invocations/build:8945511751514863186\"@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@    }@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  }, @@@",
-      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"id\": \"8945511751514863187\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@  \"id\": \"8945511751514863186\"@@@",
       "@@@STEP_LOG_LINE@fake-task-id-2-build.proto.json@}@@@",
-      "@@@STEP_LOG_END@fake-task-id-2-build.proto.json@@@"
+      "@@@STEP_LOG_END@fake-task-id-2-build.proto.json@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@{@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"status\": \"SUCCESS\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"value\": \"false\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"key\": \"cq_experimental\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  ], @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"project\": \"fuchsia\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"builder\": \"builder-subbuild2\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"bucket\": \"try\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  }, @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"createTime\": \"2018-05-25T23:50:17Z\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"output\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"cas_output_hash\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      }@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  }, @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"gerritChanges\": [@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"project\": \"fuchsia\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"host\": \"chromium-review.googlesource.com\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"patchset\": \"7\", @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@        \"change\": \"123456\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      }@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    ]@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  }, @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"priority\": 30, @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"taskId\": \"abc123\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }, @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    \"resultdb\": {@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@      \"invocation\": \"invocations/build:8945511751514863187\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@    }@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  }, @@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@  \"id\": \"8945511751514863187\"@@@",
+      "@@@STEP_LOG_LINE@fake-task-id-1-build.proto.json@}@@@",
+      "@@@STEP_LOG_END@fake-task-id-1-build.proto.json@@@"
     ]
   },
   {
@@ -808,7 +886,7 @@
       "@@@STEP_LOG_LINE@json.output@      \"bot_id\": \"vm-123\", @@@",
       "@@@STEP_LOG_LINE@json.output@      \"duration\": 62.35, @@@",
       "@@@STEP_LOG_LINE@json.output@      \"exit_code\": 0, @@@",
-      "@@@STEP_LOG_LINE@json.output@      \"name\": \"builder-subbuild1\", @@@",
+      "@@@STEP_LOG_LINE@json.output@      \"name\": \"ios_debug\", @@@",
       "@@@STEP_LOG_LINE@json.output@      \"outputs_ref\": {@@@",
       "@@@STEP_LOG_LINE@json.output@        \"isolated\": \"abc123\", @@@",
       "@@@STEP_LOG_LINE@json.output@        \"isolatedserver\": \"https://isolateserver.appspot.com\", @@@",
@@ -837,12 +915,12 @@
       "@@@STEP_LOG_LINE@json.output@  }@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LOG_LINE@task stdout+stderr: builder-subbuild1@hello world!@@@",
-      "@@@STEP_LOG_END@task stdout+stderr: builder-subbuild1@@@",
       "@@@STEP_LOG_LINE@task stdout+stderr: builder-subbuild2@hello world!@@@",
       "@@@STEP_LOG_END@task stdout+stderr: builder-subbuild2@@@",
-      "@@@STEP_LINK@task isolated outputs: builder-subbuild1@https://isolateserver.appspot.com/browse?namespace=default-gzip&hash=abc123@@@",
-      "@@@STEP_LINK@task isolated outputs: builder-subbuild2@https://isolateserver.appspot.com/browse?namespace=default-gzip&hash=abc123@@@"
+      "@@@STEP_LOG_LINE@task stdout+stderr: ios_debug@hello world!@@@",
+      "@@@STEP_LOG_END@task stdout+stderr: ios_debug@@@",
+      "@@@STEP_LINK@task isolated outputs: builder-subbuild2@https://isolateserver.appspot.com/browse?namespace=default-gzip&hash=abc123@@@",
+      "@@@STEP_LINK@task isolated outputs: ios_debug@https://isolateserver.appspot.com/browse?namespace=default-gzip&hash=abc123@@@"
     ]
   },
   {
@@ -853,7 +931,7 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "[CLEANUP]/fake-task-id-1/build.proto.json",
+      "[CLEANUP]/fake-task-id-2/build.proto.json",
       "/path/to/tmp/"
     ],
     "infra_step": true,
@@ -870,14 +948,17 @@
       "@@@STEP_LOG_LINE@build.proto.json@  ], @@@",
       "@@@STEP_LOG_LINE@build.proto.json@  \"builder\": {@@@",
       "@@@STEP_LOG_LINE@build.proto.json@    \"project\": \"fuchsia\", @@@",
-      "@@@STEP_LOG_LINE@build.proto.json@    \"builder\": \"builder-subbuild1\", @@@",
+      "@@@STEP_LOG_LINE@build.proto.json@    \"builder\": \"ios_debug\", @@@",
       "@@@STEP_LOG_LINE@build.proto.json@    \"bucket\": \"try\"@@@",
       "@@@STEP_LOG_LINE@build.proto.json@  }, @@@",
       "@@@STEP_LOG_LINE@build.proto.json@  \"createTime\": \"2018-05-25T23:50:17Z\", @@@",
       "@@@STEP_LOG_LINE@build.proto.json@  \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
       "@@@STEP_LOG_LINE@build.proto.json@  \"output\": {@@@",
       "@@@STEP_LOG_LINE@build.proto.json@    \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@build.proto.json@      \"test_orchestration_inputs_hash\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@build.proto.json@      \"cas_output_hash\": {@@@",
+      "@@@STEP_LOG_LINE@build.proto.json@        \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@build.proto.json@        \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@build.proto.json@      }@@@",
       "@@@STEP_LOG_LINE@build.proto.json@    }@@@",
       "@@@STEP_LOG_LINE@build.proto.json@  }, @@@",
       "@@@STEP_LOG_LINE@build.proto.json@  \"input\": {@@@",
@@ -915,7 +996,7 @@
       "--json-output",
       "/path/to/tmp/json",
       "copy",
-      "[CLEANUP]/fake-task-id-2/build.proto.json",
+      "[CLEANUP]/fake-task-id-1/build.proto.json",
       "/path/to/tmp/"
     ],
     "infra_step": true,
@@ -939,7 +1020,10 @@
       "@@@STEP_LOG_LINE@build.proto.json@  \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
       "@@@STEP_LOG_LINE@build.proto.json@  \"output\": {@@@",
       "@@@STEP_LOG_LINE@build.proto.json@    \"properties\": {@@@",
-      "@@@STEP_LOG_LINE@build.proto.json@      \"test_orchestration_inputs_hash\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@build.proto.json@      \"cas_output_hash\": {@@@",
+      "@@@STEP_LOG_LINE@build.proto.json@        \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@build.proto.json@        \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@build.proto.json@      }@@@",
       "@@@STEP_LOG_LINE@build.proto.json@    }@@@",
       "@@@STEP_LOG_LINE@build.proto.json@  }, @@@",
       "@@@STEP_LOG_LINE@build.proto.json@  \"input\": {@@@",
@@ -967,6 +1051,618 @@
     ]
   },
   {
+    "cmd": [],
+    "name": "launch builds (2)"
+  },
+  {
+    "cmd": [
+      "led",
+      "get-builder",
+      "luci.proj.try:Linux Staging Engine Drone"
+    ],
+    "name": "launch builds (2).led get-builder",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@proto.output@{@@@",
+      "@@@STEP_LOG_LINE@proto.output@  \"buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"bbagent_args\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@      \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"builder\": \"Linux Staging Engine Drone\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }@@@",
+      "@@@STEP_LOG_LINE@proto.output@      }@@@",
+      "@@@STEP_LOG_LINE@proto.output@    }@@@",
+      "@@@STEP_LOG_LINE@proto.output@  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@}@@@",
+      "@@@STEP_LOG_END@proto.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "led",
+      "edit",
+      "-p",
+      "tests=[{\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"scripts\": [\"out/script.sh\"]}]",
+      "-p",
+      "$recipe_engine/led={\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \"rbe_cas_input\": {\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \"digest\": {\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \"size_bytes\": 91}}}",
+      "-p",
+      "recipe=\"engine_v2/tester\"",
+      "-p",
+      "task_name=\"felt_test\"",
+      "-p",
+      "environment=\"Staging\"",
+      "-p",
+      "dependencies=[{\"dependency\": \"android_sdk\"}, {\"dependency\": \"chrome_and_driver\"}]",
+      "-p",
+      "git_url=\"http://abc\"",
+      "-p",
+      "$recipe_engine/buildbucket={\"build\": {\"builder\": {\"bucket\": \"try\", \"builder\": \"try-builder\", \"project\": \"proj\"}, \"createTime\": \"2018-05-25T23:50:17Z\", \"createdBy\": \"user:commit-bot@chromium.org\", \"id\": \"8945511751514863184\", \"infra\": {\"resultdb\": {\"invocation\": \"invocations/build:8945511751514863184\"}, \"swarming\": {\"priority\": 30}}, \"input\": {\"gerritChanges\": [{\"change\": \"123456\", \"host\": \"github.com\", \"patchset\": \"7\", \"project\": \"repo/a\"}], \"gitilesCommit\": {\"host\": \"github.com\", \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \"project\": \"repo/a\", \"ref\": \"refs/heads/main\"}}, \"number\": 123, \"tags\": [{\"key\": \"cq_experimental\", \"value\": \"false\"}]}}",
+      "-p",
+      "git_ref=\"refs/123/master\"",
+      "-p",
+      "build={\"dependencies\": [\"ios_debug\"], \"name\": \"felt_test\", \"parameters\": [\"test\"], \"resolved_deps\": [{\"ios_debug\": \"bcd\", \"web_tests\": \"abc\"}], \"scripts\": [\"out/script.sh\"]}"
+    ],
+    "name": "launch builds (2).led edit",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}\n}\n}\n}\n}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@proto.output@{@@@",
+      "@@@STEP_LOG_LINE@proto.output@  \"buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"bbagent_args\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@      \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"builder\": \"Linux Staging Engine Drone\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"priority\": -20@@@",
+      "@@@STEP_LOG_LINE@proto.output@          }@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"$recipe_engine/buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"builder\": \"try-builder\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"createTime\": \"2018-05-25T23:50:17Z\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"id\": \"8945511751514863184\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"resultdb\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"invocation\": \"invocations/build:8945511751514863184\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"priority\": 30.0@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"gerritChanges\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"change\": \"123456\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"host\": \"github.com\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"patchset\": \"7\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"project\": \"repo/a\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"host\": \"github.com\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"project\": \"repo/a\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"ref\": \"refs/heads/main\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"number\": 123.0, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"key\": \"cq_experimental\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"value\": \"false\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"$recipe_engine/led\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"rbe_cas_input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"digest\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"size_bytes\": 91.0@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"resolved_deps\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependency\": \"android_sdk\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependency\": \"chrome_and_driver\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"environment\": \"Staging\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/tester\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@          }@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }@@@",
+      "@@@STEP_LOG_LINE@proto.output@      }@@@",
+      "@@@STEP_LOG_LINE@proto.output@    }@@@",
+      "@@@STEP_LOG_LINE@proto.output@  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@}@@@",
+      "@@@STEP_LOG_END@proto.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "led",
+      "edit",
+      "-name",
+      "felt_test"
+    ],
+    "name": "launch builds (2).led edit (2)",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"resolved_deps\": [\n{\n\"ios_debug\": \"bcd\", \n\"web_tests\": \"abc\"\n}\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/tester\", \n\"task_name\": \"felt_test\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}\n}\n}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@proto.output@{@@@",
+      "@@@STEP_LOG_LINE@proto.output@  \"buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"bbagent_args\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@      \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"builder\": \"Linux Staging Engine Drone\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"priority\": -20@@@",
+      "@@@STEP_LOG_LINE@proto.output@          }@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"$recipe_engine/buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"builder\": \"try-builder\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"createTime\": \"2018-05-25T23:50:17Z\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"id\": \"8945511751514863184\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"resultdb\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"invocation\": \"invocations/build:8945511751514863184\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"priority\": 30.0@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"gerritChanges\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"change\": \"123456\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"host\": \"github.com\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"patchset\": \"7\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"project\": \"repo/a\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"host\": \"github.com\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"project\": \"repo/a\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"ref\": \"refs/heads/main\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"number\": 123.0, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"key\": \"cq_experimental\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"value\": \"false\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"$recipe_engine/led\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"rbe_cas_input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"digest\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"size_bytes\": 91.0@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"resolved_deps\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependency\": \"android_sdk\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependency\": \"chrome_and_driver\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"environment\": \"Staging\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/tester\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@          }@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }@@@",
+      "@@@STEP_LOG_LINE@proto.output@      }@@@",
+      "@@@STEP_LOG_LINE@proto.output@    }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"name\": \"felt_test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@}@@@",
+      "@@@STEP_LOG_END@proto.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "led",
+      "edit",
+      "-r",
+      "engine_v2/tester"
+    ],
+    "name": "launch builds (2).led edit (3)",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"resolved_deps\": [\n{\n\"ios_debug\": \"bcd\", \n\"web_tests\": \"abc\"\n}\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/tester\", \n\"task_name\": \"felt_test\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}, \n\"name\": \"felt_test\"\n}\n}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@proto.output@{@@@",
+      "@@@STEP_LOG_LINE@proto.output@  \"buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"bbagent_args\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@      \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"builder\": \"Linux Staging Engine Drone\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"priority\": -20@@@",
+      "@@@STEP_LOG_LINE@proto.output@          }@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"$recipe_engine/buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"builder\": \"try-builder\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"createTime\": \"2018-05-25T23:50:17Z\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"id\": \"8945511751514863184\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"resultdb\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"invocation\": \"invocations/build:8945511751514863184\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"priority\": 30.0@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"gerritChanges\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"change\": \"123456\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"host\": \"github.com\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"patchset\": \"7\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"project\": \"repo/a\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"host\": \"github.com\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"project\": \"repo/a\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"ref\": \"refs/heads/main\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"number\": 123.0, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"key\": \"cq_experimental\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"value\": \"false\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"$recipe_engine/led\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"rbe_cas_input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"digest\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"size_bytes\": 91.0@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"resolved_deps\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependency\": \"android_sdk\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependency\": \"chrome_and_driver\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"environment\": \"Staging\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/tester\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@          }@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }@@@",
+      "@@@STEP_LOG_LINE@proto.output@      }@@@",
+      "@@@STEP_LOG_LINE@proto.output@    }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"name\": \"felt_test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@}@@@",
+      "@@@STEP_LOG_END@proto.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "led",
+      "edit",
+      "-rbh",
+      "146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e/91"
+    ],
+    "name": "launch builds (2).led edit (4)",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"resolved_deps\": [\n{\n\"ios_debug\": \"bcd\", \n\"web_tests\": \"abc\"\n}\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/tester\", \n\"task_name\": \"felt_test\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}, \n\"name\": \"felt_test\"\n}\n}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@proto.output@{@@@",
+      "@@@STEP_LOG_LINE@proto.output@  \"buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"bbagent_args\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@      \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"builder\": \"Linux Staging Engine Drone\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"priority\": -20@@@",
+      "@@@STEP_LOG_LINE@proto.output@          }@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@        \"input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@          \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"$recipe_engine/buildbucket\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"bucket\": \"try\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"builder\": \"try-builder\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"project\": \"proj\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"createTime\": \"2018-05-25T23:50:17Z\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"createdBy\": \"user:commit-bot@chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"id\": \"8945511751514863184\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"infra\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"resultdb\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"invocation\": \"invocations/build:8945511751514863184\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"priority\": 30.0@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"gerritChanges\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"change\": \"123456\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"host\": \"github.com\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"patchset\": \"7\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                      \"project\": \"repo/a\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"host\": \"github.com\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"project\": \"repo/a\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"ref\": \"refs/heads/main\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"number\": 123.0, @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"key\": \"cq_experimental\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                    \"value\": \"false\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"$recipe_engine/led\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"rbe_cas_input\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"digest\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"size_bytes\": 91.0@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"build\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"resolved_deps\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\": \"bcd\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"web_tests\": \"abc\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                }@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@              \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@            }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependency\": \"android_sdk\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependency\": \"chrome_and_driver\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"environment\": \"Staging\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"git_ref\": \"refs/123/master\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"git_url\": \"http://abc\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"recipe\": \"engine_v2/tester\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"task_name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@            \"tests\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@              {@@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"dependencies\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"ios_debug\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"name\": \"felt_test\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"parameters\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ], @@@",
+      "@@@STEP_LOG_LINE@proto.output@                \"scripts\": [@@@",
+      "@@@STEP_LOG_LINE@proto.output@                  \"out/script.sh\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@                ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@              }@@@",
+      "@@@STEP_LOG_LINE@proto.output@            ]@@@",
+      "@@@STEP_LOG_LINE@proto.output@          }@@@",
+      "@@@STEP_LOG_LINE@proto.output@        }@@@",
+      "@@@STEP_LOG_LINE@proto.output@      }@@@",
+      "@@@STEP_LOG_LINE@proto.output@    }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"name\": \"felt_test\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@  }, @@@",
+      "@@@STEP_LOG_LINE@proto.output@  \"cas_user_payload\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@    \"digest\": {@@@",
+      "@@@STEP_LOG_LINE@proto.output@      \"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", @@@",
+      "@@@STEP_LOG_LINE@proto.output@      \"size_bytes\": \"91\"@@@",
+      "@@@STEP_LOG_LINE@proto.output@    }@@@",
+      "@@@STEP_LOG_LINE@proto.output@  }@@@",
+      "@@@STEP_LOG_LINE@proto.output@}@@@",
+      "@@@STEP_LOG_END@proto.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "led",
+      "launch",
+      "-modernize"
+    ],
+    "name": "launch builds (2).led launch",
+    "stdin": "{\n\"buildbucket\": {\n\"bbagent_args\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"Linux Staging Engine Drone\", \n\"project\": \"proj\"\n}, \n\"infra\": {\n\"swarming\": {\n\"priority\": -20\n}\n}, \n\"input\": {\n\"properties\": {\n\"$recipe_engine/buildbucket\": {\n\"build\": {\n\"builder\": {\n\"bucket\": \"try\", \n\"builder\": \"try-builder\", \n\"project\": \"proj\"\n}, \n\"createTime\": \"2018-05-25T23:50:17Z\", \n\"createdBy\": \"user:commit-bot@chromium.org\", \n\"id\": \"8945511751514863184\", \n\"infra\": {\n\"resultdb\": {\n\"invocation\": \"invocations/build:8945511751514863184\"\n}, \n\"swarming\": {\n\"priority\": 30.0\n}\n}, \n\"input\": {\n\"gerritChanges\": [\n{\n\"change\": \"123456\", \n\"host\": \"github.com\", \n\"patchset\": \"7\", \n\"project\": \"repo/a\"\n}\n], \n\"gitilesCommit\": {\n\"host\": \"github.com\", \n\"id\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\", \n\"project\": \"repo/a\", \n\"ref\": \"refs/heads/main\"\n}\n}, \n\"number\": 123.0, \n\"tags\": [\n{\n\"key\": \"cq_experimental\", \n\"value\": \"false\"\n}\n]\n}\n}, \n\"$recipe_engine/led\": {\n\"led_run_id\": \"flutter/led/abc_google.com/b9861e3db1034eee460599837221ab468e03bc43f9fd05684a08157fd646abfc\", \n\"rbe_cas_input\": {\n\"cas_instance\": \"projects/chromium-swarm/instances/default_instance\", \n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": 91.0\n}\n}\n}, \n\"build\": {\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"resolved_deps\": [\n{\n\"ios_debug\": \"bcd\", \n\"web_tests\": \"abc\"\n}\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}, \n\"dependencies\": [\n{\n\"dependency\": \"android_sdk\"\n}, \n{\n\"dependency\": \"chrome_and_driver\"\n}\n], \n\"environment\": \"Staging\", \n\"git_ref\": \"refs/123/master\", \n\"git_url\": \"http://abc\", \n\"recipe\": \"engine_v2/tester\", \n\"task_name\": \"felt_test\", \n\"tests\": [\n{\n\"dependencies\": [\n\"ios_debug\"\n], \n\"name\": \"felt_test\", \n\"parameters\": [\n\"test\"\n], \n\"scripts\": [\n\"out/script.sh\"\n]\n}\n]\n}\n}\n}\n}, \n\"name\": \"felt_test\"\n}, \n\"cas_user_payload\": {\n\"digest\": {\n\"hash\": \"146d56311043bb141309968d570e23d05a108d13ce2e20b5aeb40a9b95629b3e\", \n\"size_bytes\": \"91\"\n}\n}\n}",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"swarming\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"host_name\": \"chromium-swarm.appspot.com\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"task_id\": \"fake-task-id\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@Swarming task@https://chromium-swarm.appspot.com/task?id=fake-task-id@@@"
+    ]
+  },
+  {
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipe_modules/shard_util_v2/examples/full.py b/recipe_modules/shard_util_v2/examples/full.py
index 55e833f..a565a78 100644
--- a/recipe_modules/shard_util_v2/examples/full.py
+++ b/recipe_modules/shard_util_v2/examples/full.py
@@ -22,33 +22,42 @@
 
 def RunSteps(api):
   build_configs = api.properties.get('builds', [])
+  test_configs = api.properties.get('tests', [])
   with api.step.nest("launch builds") as presentation:
-    reqs = api.shard_util_v2.schedule(build_configs, presentation)
+    reqs = api.shard_util_v2.schedule_builds(build_configs, presentation)
   with api.step.nest("collect builds") as presentation:
     builds = api.shard_util_v2.collect(reqs, presentation)
     for build in builds.itervalues():
       if build.build_proto.status != common_pb2.SUCCESS:
         raise api.step.StepFailure("build %s failed" % build.build_id)
+  with api.step.nest("launch builds") as presentation:
+    reqs = api.shard_util_v2.schedule_tests(test_configs, builds, presentation)
 
 
 def GenTests(api):
   try_subbuild1 = api.shard_util_v2.try_build_message(
       build_id=8945511751514863186,
-      builder="builder-subbuild1",
+      builder="ios_debug",
       input_props={'task_name': 'mytask'},
-      output_props={"test_orchestration_inputs_hash": "abc"},
+      output_props={
+          "cas_output_hash": {"web_tests": "abc", "ios_debug": "bcd"}
+      },
       status="SUCCESS",
   )
   try_subbuild2 = api.shard_util_v2.try_build_message(
       build_id=8945511751514863187,
       builder="builder-subbuild2",
-      output_props={"test_orchestration_inputs_hash": "abc"},
+      output_props={
+          "cas_output_hash": {"web_tests": "abc", "ios_debug": "bcd"}
+      },
       status="SUCCESS",
   )
   try_failure = api.shard_util_v2.try_build_message(
       build_id=8945511751514863187,
       builder="builder-subbuild2",
-      output_props={"test_orchestration_inputs_hash": "abc"},
+      output_props={
+          "cas_output_hash": {"web_tests": "abc", "ios_debug": "bcd"}
+      },
       status="FAILURE",
   )
 
@@ -57,6 +66,10 @@
           "name": "ios_debug", "gn": [], "ninja": ["ios_debug"],
           'drone_dimensions': ['dimension1=abc']
       }],
+      'tests': [{
+          "name": "felt_test", "dependencies": ["ios_debug"],
+          "scripts": ["out/script.sh"], "parameters": ["test"]
+      }],
       'environment': 'Staging',
       'dependencies': [{"dependency": "android_sdk"},
                        {"dependency": "chrome_and_driver"}],
@@ -76,12 +89,14 @@
       },
   }
   props_bb = {
-      'task_name': 'mytask',
-      'builds': [{
+      'task_name': 'mytask', 'builds': [{
           "name": "ios_debug", "gn": ["--ios"],
           "ninja": {"config": "ios_debug",
                     "targets": []}, 'drone_dimensions': ['dimension1=abc'],
           "generators": [{"name": "generator1", "script": "script1.sh"}]
+      }], 'tests': [{
+          "name": "felt_test", "dependencies": ["ios_debug"],
+          "scripts": ["out/script.sh"], "parameters": ["test"]
       }], 'dependencies': [{"dependency": "android_sdk"},
                            {"dependency": "chrome_and_driver"}],
       'environment': 'Staging'
diff --git a/recipes/engine_v2/engine_v2.expected/basic.json b/recipes/engine_v2/engine_v2.expected/basic.json
index 2b1af47..cba0b35 100644
--- a/recipes/engine_v2/engine_v2.expected/basic.json
+++ b/recipes/engine_v2/engine_v2.expected/basic.json
@@ -341,6 +341,32 @@
     ]
   },
   {
+    "cmd": [
+      "python",
+      "-u",
+      "\nimport shutil\nimport sys\nshutil.copy(sys.argv[1], sys.argv[2])\n",
+      "[START_DIR]/engine/ci/builders/None.json",
+      "/path/to/tmp/json"
+    ],
+    "name": "Read build config file",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "launch tests"
+  },
+  {
+    "cmd": [],
+    "name": "collect tests"
+  },
+  {
+    "cmd": [],
+    "name": "display tests"
+  },
+  {
     "name": "$result"
   }
 ]
\ No newline at end of file
diff --git a/recipes/engine_v2/engine_v2.py b/recipes/engine_v2/engine_v2.py
index 7d29ecf..1170b34 100644
--- a/recipes/engine_v2/engine_v2.py
+++ b/recipes/engine_v2/engine_v2.py
@@ -53,17 +53,36 @@
   builds = builds or api.json.read(
       'Read build config file',
       config_path,
-      step_test_data=lambda: api.json.test_api.output([])
-  ).json.output
+      step_test_data=lambda: api.json.test_api.output({})
+  ).json.output.get('builds', [])
   with api.step.nest('launch builds') as presentation:
-    tasks = api.shard_util_v2.schedule(builds, presentation)
+    tasks = api.shard_util_v2.schedule_builds(builds, presentation)
   with api.step.nest('collect builds') as presentation:
     results = api.shard_util_v2.collect(tasks, presentation)
+
   api.display_util.display_builds(
       step_name='display builds',
       builds=[b.build_proto for b in results.values()],
-        raise_on_failure=True,
-    )
+      raise_on_failure=True,
+  )
+
+  # Run tests
+  tests = api.properties.get('tests')
+  tests = tests or api.json.read(
+      'Read build config file',
+      config_path,
+      step_test_data=lambda: api.json.test_api.output({})
+  ).json.output.get('tests', [])
+  with api.step.nest('launch tests') as presentation:
+    tasks = api.shard_util_v2.schedule_tests(tests, results, presentation)
+  with api.step.nest('collect tests') as presentation:
+    results = api.shard_util_v2.collect(tasks, presentation)
+
+  api.display_util.display_builds(
+      step_name='display tests',
+      builds=[b.build_proto for b in results.values()],
+      raise_on_failure=True,
+  )
 
 
 def GenTests(api):
@@ -80,7 +99,8 @@
   }]
 
   yield api.test(
-      'basic', api.properties(builds=builds, environment='Staging'),
+      'basic',
+      api.properties(builds=builds, environment='Staging'),
       api.buildbucket.try_build(
           project='proj',
           builder='try-builder',
diff --git a/recipes/engine_v2/engine_v2_2_4_0.py b/recipes/engine_v2/engine_v2_2_4_0.py
index 8cd36de..010651a 100644
--- a/recipes/engine_v2/engine_v2_2_4_0.py
+++ b/recipes/engine_v2/engine_v2_2_4_0.py
@@ -34,7 +34,7 @@
 def RunSteps(api, properties, env_properties):
   builds = api.properties.get('builds')
   with api.step.nest('launch builds') as presentation:
-    tasks = api.shard_util_v2.schedule(builds, presentation)
+    tasks = api.shard_util_v2.schedule_builds(builds, presentation)
   with api.step.nest('collect builds') as presentation:
     api.shard_util_v2.collect(tasks, presentation)
 
diff --git a/recipes/engine_v2/engine_v2_2_5_0.py b/recipes/engine_v2/engine_v2_2_5_0.py
index b87064f..15ff14f 100644
--- a/recipes/engine_v2/engine_v2_2_5_0.py
+++ b/recipes/engine_v2/engine_v2_2_5_0.py
@@ -53,7 +53,7 @@
       step_test_data=lambda: api.json.test_api.output([])
   ).json.output
   with api.step.nest('launch builds') as presentation:
-    tasks = api.shard_util_v2.schedule(builds, presentation)
+    tasks = api.shard_util_v2.schedule_builds(builds, presentation)
   with api.step.nest('collect builds') as presentation:
     api.shard_util_v2.collect(tasks, presentation)