[cbuild] Add support for regression data

If the cbuild sends a JSON message in the form below, it will be parsed
and additional information will be sent to Gerrit.

```json
{
  "result" : false,
  "regression": true
}
```

The status message in Gerrit will then say
"FAILURE (REGRESSIONS DETECTED)" if regression is `true` or
"FAILURE (NO REGRESSIONS DETECTED)" when regression is `false`.

Legacy results are not changed, but support for them will be removed in
a follow-up CL once they are drained from Cloud Storage and the cbuild
service is switched to provide the new format.

Bug:b/167667100
Change-Id: I7f48ca5cfbb7d7b15a213f7a0ab4f05a3fca5b1a
Reviewed-on: https://dart-review.googlesource.com/c/recipes/+/171560
Reviewed-by: Karl Klose <karlklose@google.com>
Commit-Queue: Alexander Thomas <athom@google.com>
diff --git a/README.recipes.md b/README.recipes.md
index c106a51..132a641 100644
--- a/README.recipes.md
+++ b/README.recipes.md
@@ -115,7 +115,7 @@
 &mdash; **def [RunSteps](/recipes/cron/cron.py#28)(api, properties):**
 ### *recipes* / [dart/cbuild](/recipes/dart/cbuild.py)
 
-[DEPS](/recipes/dart/cbuild.py#21): [depot\_tools/gitiles][depot_tools/recipe_modules/gitiles], [depot\_tools/gsutil][depot_tools/recipe_modules/gsutil], [fuchsia/gerrit][fuchsia/recipe_modules/gerrit], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+[DEPS](/recipes/dart/cbuild.py#21): [depot\_tools/gitiles][depot_tools/recipe_modules/gitiles], [depot\_tools/gsutil][depot_tools/recipe_modules/gsutil], [fuchsia/gerrit][fuchsia/recipe_modules/gerrit], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step]
 
 A recipe that processes go/dart-cbuild results.
 
@@ -125,7 +125,7 @@
 gerrit change for the commit, if any. Finally, the result is deleted from the
 GCS bucket so that it won't be processed again by the next run.
 
-&mdash; **def [RunSteps](/recipes/dart/cbuild.py#46)(api):**
+&mdash; **def [RunSteps](/recipes/dart/cbuild.py#47)(api):**
 ### *recipes* / [dart/chocolatey](/recipes/dart/chocolatey.py)
 
 [DEPS](/recipes/dart/chocolatey.py#11): [dart](#recipe_modules-dart), [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step], [recipe\_engine/url][recipe_engine/recipe_modules/url]
@@ -232,9 +232,9 @@
 &mdash; **def [RunSteps](/recipes/roller/lkgr.py#33)(api, properties):**
 ### *recipes* / [roller/roll\_to\_dev](/recipes/roller/roll_to_dev.py)
 
-[DEPS](/recipes/roller/roll_to_dev.py#9): [depot\_tools/bot\_update][depot_tools/recipe_modules/bot_update], [depot\_tools/gclient][depot_tools/recipe_modules/gclient], [depot\_tools/git][depot_tools/recipe_modules/git], [depot\_tools/gitiles][depot_tools/recipe_modules/gitiles], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step], [recipe\_engine/time][recipe_engine/recipe_modules/time]
+[DEPS](/recipes/roller/roll_to_dev.py#9): [depot\_tools/bot\_update][depot_tools/recipe_modules/bot_update], [depot\_tools/gclient][depot_tools/recipe_modules/gclient], [depot\_tools/git][depot_tools/recipe_modules/git], [depot\_tools/gitiles][depot_tools/recipe_modules/gitiles], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step], [recipe\_engine/time][recipe_engine/recipe_modules/time]
 
-&mdash; **def [RunSteps](/recipes/roller/roll_to_dev.py#38)(api, properties):**
+&mdash; **def [RunSteps](/recipes/roller/roll_to_dev.py#36)(api, properties):**
 
 [build/recipe_modules/goma]: https://chromium.googlesource.com/chromium/tools/build.git/+/d37f5069b5b2060363168184fb674aedafd5a09d/recipes/README.recipes.md#recipe_modules-goma
 [depot_tools/recipe_modules/bot_update]: https://chromium.googlesource.com/chromium/tools/depot_tools.git/+/8630bb119f964fe6faa6d313cb6b6070f80142d7/recipes/README.recipes.md#recipe_modules-bot_update
diff --git a/recipes/dart/cbuild.expected/experimental-with-results.json b/recipes/dart/cbuild.expected/experimental-with-results.json
index 31da076..13460aa 100644
--- a/recipes/dart/cbuild.expected/experimental-with-results.json
+++ b/recipes/dart/cbuild.expected/experimental-with-results.json
@@ -23,13 +23,37 @@
       "----",
       "cp",
       "gs://dart-cbuild-test-results/hash-of-success",
-      "/path/to/tmp/"
+      "/path/to/tmp/json"
     ],
     "infra_step": true,
     "name": "gsutil download cbuild result for hash-of-success",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@raw_io.output_text[result]@true@@@",
-      "@@@STEP_LOG_END@raw_io.output_text[result]@@@"
+      "@@@STEP_LOG_LINE@json.output[result]@{@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"result\": true@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@}@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://dart-cbuild-test-results/hash-of-regression",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "gsutil download cbuild result for hash-of-regression",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output[result]@{@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"regression\": true, @@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"result\": false@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@}@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
     ]
   },
   {
@@ -42,13 +66,16 @@
       "----",
       "cp",
       "gs://dart-cbuild-test-results/hash-of-failure",
-      "/path/to/tmp/"
+      "/path/to/tmp/json"
     ],
     "infra_step": true,
     "name": "gsutil download cbuild result for hash-of-failure",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@raw_io.output_text[result]@false@@@",
-      "@@@STEP_LOG_END@raw_io.output_text[result]@@@"
+      "@@@STEP_LOG_LINE@json.output[result]@{@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"regression\": false, @@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"result\": false@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@}@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
     ]
   },
   {
@@ -60,7 +87,7 @@
     ],
     "infra_step": true,
     "name": "buildbucket.schedule",
-    "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-success\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"SUCCESS\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-success\"}, \"requestId\": \"0-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}, {\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-failure\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"FAILURE\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-failure\"}, \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+    "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-success\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"SUCCESS\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-success\"}, \"requestId\": \"0-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}, {\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-regression\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"FAILURE\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-regression\"}, \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}, {\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-failure\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"FAILURE\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-failure\"}, \"requestId\": \"0-00000000-0000-0000-0000-00000000133d\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
     "~followup_annotations": [
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"responses\": [@@@",
@@ -81,6 +108,15 @@
       "@@@STEP_LOG_LINE@json.output@        }, @@@",
       "@@@STEP_LOG_LINE@json.output@        \"id\": \"8922054662172514001\"@@@",
       "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@json.output@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@json.output@        }, @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"id\": \"8922054662172514002\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
       "@@@STEP_LOG_LINE@json.output@    }@@@",
       "@@@STEP_LOG_LINE@json.output@  ]@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
@@ -124,6 +160,33 @@
       "@@@STEP_LOG_LINE@request@        \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
       "@@@STEP_LOG_LINE@request@        \"gitilesCommit\": {@@@",
       "@@@STEP_LOG_LINE@request@          \"host\": \"dart.googlesource.com\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"id\": \"hash-of-regression\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"project\": \"sdk\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"ref\": \"refs/heads/master\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"result\": \"FAILURE\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-regression\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@request@          {@@@",
+      "@@@STEP_LOG_LINE@request@            \"key\": \"user_agent\", @@@",
+      "@@@STEP_LOG_LINE@request@            \"value\": \"recipe\"@@@",
+      "@@@STEP_LOG_LINE@request@          }@@@",
+      "@@@STEP_LOG_LINE@request@        ]@@@",
+      "@@@STEP_LOG_LINE@request@      }@@@",
+      "@@@STEP_LOG_LINE@request@    }, @@@",
+      "@@@STEP_LOG_LINE@request@    {@@@",
+      "@@@STEP_LOG_LINE@request@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@request@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"experimental\": \"NO\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"host\": \"dart.googlesource.com\", @@@",
       "@@@STEP_LOG_LINE@request@          \"id\": \"hash-of-failure\", @@@",
       "@@@STEP_LOG_LINE@request@          \"project\": \"sdk\", @@@",
       "@@@STEP_LOG_LINE@request@          \"ref\": \"refs/heads/master\"@@@",
@@ -132,7 +195,7 @@
       "@@@STEP_LOG_LINE@request@          \"result\": \"FAILURE\", @@@",
       "@@@STEP_LOG_LINE@request@          \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-failure\"@@@",
       "@@@STEP_LOG_LINE@request@        }, @@@",
-      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-00000000133d\", @@@",
       "@@@STEP_LOG_LINE@request@        \"tags\": [@@@",
       "@@@STEP_LOG_LINE@request@          {@@@",
       "@@@STEP_LOG_LINE@request@            \"key\": \"user_agent\", @@@",
@@ -145,7 +208,8 @@
       "@@@STEP_LOG_LINE@request@}@@@",
       "@@@STEP_LOG_END@request@@@",
       "@@@STEP_LINK@8922054662172514000@https://cr-buildbucket.appspot.com/build/8922054662172514000@@@",
-      "@@@STEP_LINK@8922054662172514001@https://cr-buildbucket.appspot.com/build/8922054662172514001@@@"
+      "@@@STEP_LINK@8922054662172514001@https://cr-buildbucket.appspot.com/build/8922054662172514001@@@",
+      "@@@STEP_LINK@8922054662172514002@https://cr-buildbucket.appspot.com/build/8922054662172514002@@@"
     ]
   },
   {
@@ -192,6 +256,42 @@
       "--json-file",
       "/path/to/tmp/json",
       "--url",
+      "https://dart.googlesource.com/sdk/+/hash-of-regression",
+      "--format",
+      "json"
+    ],
+    "name": "download change-id for hash-of-regression",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"commit\": \"7c6ab322bd26f369fb93303a1c866f79ec59a48d\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Subject\\n\\nChange-Id: Ihash-of-regression\\n\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"f90914c0e844c152907d395449631c07d536a670\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  ], @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree\": \"ef3e32d5c0c7a44c7af721dd17ebf0ff671a4596\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree_diff\": []@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gitiles]/resources/gerrit_client.py",
+      "--json-file",
+      "/path/to/tmp/json",
+      "--url",
       "https://dart.googlesource.com/sdk/+/hash-of-failure",
       "--format",
       "json"
diff --git a/recipes/dart/cbuild.expected/with-legacy-results.json b/recipes/dart/cbuild.expected/with-legacy-results.json
new file mode 100644
index 0000000..8280eaa
--- /dev/null
+++ b/recipes/dart/cbuild.expected/with-legacy-results.json
@@ -0,0 +1,490 @@
+[
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "ls",
+      "gs://dart-cbuild-test-results"
+    ],
+    "infra_step": true,
+    "name": "gsutil find cbuild results"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://dart-cbuild-test-results/hash-of-success",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "gsutil download cbuild result for hash-of-success",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output[result]@true@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://dart-cbuild-test-results/hash-of-regression",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "gsutil download cbuild result for hash-of-regression",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output[result]@false@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://dart-cbuild-test-results/hash-of-failure",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "gsutil download cbuild result for hash-of-failure",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output[result]@false@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "bb",
+      "batch",
+      "-host",
+      "cr-buildbucket.appspot.com"
+    ],
+    "infra_step": true,
+    "name": "buildbucket.schedule",
+    "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-success\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"SUCCESS\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-success\"}, \"requestId\": \"0-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}, {\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-regression\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"FAILURE\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-regression\"}, \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}, {\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-failure\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"FAILURE\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-failure\"}, \"requestId\": \"0-00000000-0000-0000-0000-00000000133d\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"responses\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@json.output@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@json.output@        }, @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"id\": \"8922054662172514000\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@json.output@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@json.output@        }, @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"id\": \"8922054662172514001\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@json.output@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@json.output@        }, @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"id\": \"8922054662172514002\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    }@@@",
+      "@@@STEP_LOG_LINE@json.output@  ]@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LOG_LINE@request@{@@@",
+      "@@@STEP_LOG_LINE@request@  \"requests\": [@@@",
+      "@@@STEP_LOG_LINE@request@    {@@@",
+      "@@@STEP_LOG_LINE@request@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@request@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"experimental\": \"NO\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"host\": \"dart.googlesource.com\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"id\": \"hash-of-success\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"project\": \"sdk\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"ref\": \"refs/heads/master\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"result\": \"SUCCESS\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-success\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-000000001337\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@request@          {@@@",
+      "@@@STEP_LOG_LINE@request@            \"key\": \"user_agent\", @@@",
+      "@@@STEP_LOG_LINE@request@            \"value\": \"recipe\"@@@",
+      "@@@STEP_LOG_LINE@request@          }@@@",
+      "@@@STEP_LOG_LINE@request@        ]@@@",
+      "@@@STEP_LOG_LINE@request@      }@@@",
+      "@@@STEP_LOG_LINE@request@    }, @@@",
+      "@@@STEP_LOG_LINE@request@    {@@@",
+      "@@@STEP_LOG_LINE@request@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@request@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"experimental\": \"NO\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"host\": \"dart.googlesource.com\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"id\": \"hash-of-regression\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"project\": \"sdk\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"ref\": \"refs/heads/master\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"result\": \"FAILURE\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-regression\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@request@          {@@@",
+      "@@@STEP_LOG_LINE@request@            \"key\": \"user_agent\", @@@",
+      "@@@STEP_LOG_LINE@request@            \"value\": \"recipe\"@@@",
+      "@@@STEP_LOG_LINE@request@          }@@@",
+      "@@@STEP_LOG_LINE@request@        ]@@@",
+      "@@@STEP_LOG_LINE@request@      }@@@",
+      "@@@STEP_LOG_LINE@request@    }, @@@",
+      "@@@STEP_LOG_LINE@request@    {@@@",
+      "@@@STEP_LOG_LINE@request@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@request@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"experimental\": \"NO\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"host\": \"dart.googlesource.com\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"id\": \"hash-of-failure\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"project\": \"sdk\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"ref\": \"refs/heads/master\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"result\": \"FAILURE\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-failure\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-00000000133d\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@request@          {@@@",
+      "@@@STEP_LOG_LINE@request@            \"key\": \"user_agent\", @@@",
+      "@@@STEP_LOG_LINE@request@            \"value\": \"recipe\"@@@",
+      "@@@STEP_LOG_LINE@request@          }@@@",
+      "@@@STEP_LOG_LINE@request@        ]@@@",
+      "@@@STEP_LOG_LINE@request@      }@@@",
+      "@@@STEP_LOG_LINE@request@    }@@@",
+      "@@@STEP_LOG_LINE@request@  ]@@@",
+      "@@@STEP_LOG_LINE@request@}@@@",
+      "@@@STEP_LOG_END@request@@@",
+      "@@@STEP_LINK@8922054662172514000@https://cr-buildbucket.appspot.com/build/8922054662172514000@@@",
+      "@@@STEP_LINK@8922054662172514001@https://cr-buildbucket.appspot.com/build/8922054662172514001@@@",
+      "@@@STEP_LINK@8922054662172514002@https://cr-buildbucket.appspot.com/build/8922054662172514002@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gitiles]/resources/gerrit_client.py",
+      "--json-file",
+      "/path/to/tmp/json",
+      "--url",
+      "https://dart.googlesource.com/sdk/+/hash-of-success",
+      "--format",
+      "json"
+    ],
+    "name": "download change-id for hash-of-success",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"commit\": \"0d2bb3f76c86347700efe8a5d936e7715f539fae\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Subject\\n\\nChange-Id: Ihash-of-success\\n\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"1d344d4425164843176607d23dd70cdb01a45e45\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  ], @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree\": \"da90f74c609fe03335481cb8e21eaaca85602e49\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree_diff\": []@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [],
+    "name": "install infra/tools/luci/gerrit"
+  },
+  {
+    "cmd": [
+      "vpython",
+      "-u",
+      "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
+      "--json-output",
+      "/path/to/tmp/json",
+      "ensure-directory",
+      "--mode",
+      "0777",
+      "[CACHE]/cipd/infra/tools/luci/gerrit/latest"
+    ],
+    "infra_step": true,
+    "name": "install infra/tools/luci/gerrit.ensure package directory",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "cipd",
+      "ensure",
+      "-root",
+      "[CACHE]/cipd/infra/tools/luci/gerrit/latest",
+      "-ensure-file",
+      "infra/tools/luci/gerrit/${platform} latest",
+      "-max-threads",
+      "0",
+      "-json-output",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "install infra/tools/luci/gerrit.ensure_installed",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"result\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@      {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"instance_id\": \"resolved-instance_id-of-latest----------\", @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"package\": \"infra/tools/luci/gerrit/resolved-platform\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    ]@@@",
+      "@@@STEP_LOG_LINE@json.output@  }@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[CACHE]/cipd/infra/tools/luci/gerrit/latest/gerrit",
+      "set-review",
+      "-host",
+      "https://dart-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"Ihash-of-success\", \"input\": {\"message\": \"go/dart-cbuild result: SUCCESS\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-success\\n\"}, \"revision_id\": \"current\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "update gerrit for hash-of-success",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@No JSON object could be decoded@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"Ihash-of-success\", @@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"go/dart-cbuild result: SUCCESS\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-success\\n\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }, @@@",
+      "@@@STEP_LOG_LINE@json.input@  \"revision_id\": \"current\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "rm",
+      "gs://dart-cbuild-test-results/hash-of-success"
+    ],
+    "infra_step": true,
+    "name": "gsutil delete result for hash-of-success"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gitiles]/resources/gerrit_client.py",
+      "--json-file",
+      "/path/to/tmp/json",
+      "--url",
+      "https://dart.googlesource.com/sdk/+/hash-of-regression",
+      "--format",
+      "json"
+    ],
+    "name": "download change-id for hash-of-regression",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"commit\": \"7c6ab322bd26f369fb93303a1c866f79ec59a48d\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Subject\\n\\nChange-Id: Ihash-of-regression\\n\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"f90914c0e844c152907d395449631c07d536a670\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  ], @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree\": \"ef3e32d5c0c7a44c7af721dd17ebf0ff671a4596\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree_diff\": []@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[CACHE]/cipd/infra/tools/luci/gerrit/latest/gerrit",
+      "set-review",
+      "-host",
+      "https://dart-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"Ihash-of-regression\", \"input\": {\"message\": \"go/dart-cbuild result: FAILURE\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-regression\\n\"}, \"revision_id\": \"current\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "update gerrit for hash-of-regression",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@No JSON object could be decoded@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"Ihash-of-regression\", @@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"go/dart-cbuild result: FAILURE\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-regression\\n\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }, @@@",
+      "@@@STEP_LOG_LINE@json.input@  \"revision_id\": \"current\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "rm",
+      "gs://dart-cbuild-test-results/hash-of-regression"
+    ],
+    "infra_step": true,
+    "name": "gsutil delete result for hash-of-regression"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gitiles]/resources/gerrit_client.py",
+      "--json-file",
+      "/path/to/tmp/json",
+      "--url",
+      "https://dart.googlesource.com/sdk/+/hash-of-failure",
+      "--format",
+      "json"
+    ],
+    "name": "download change-id for hash-of-failure",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"commit\": \"7a8a22c2b56c47004f5f57f56c246a3cf62a4b85\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Subject\\n\\nChange-Id: Ihash-of-failure\\n\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"4913a1bdba38942821330018264de93863879a73\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  ], @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree\": \"9b421a2b9d5ccc2a8e5585bdd05db6aed18b7932\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree_diff\": []@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[CACHE]/cipd/infra/tools/luci/gerrit/latest/gerrit",
+      "set-review",
+      "-host",
+      "https://dart-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"Ihash-of-failure\", \"input\": {\"message\": \"go/dart-cbuild result: FAILURE\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-failure\\n\"}, \"revision_id\": \"current\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "update gerrit for hash-of-failure",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@No JSON object could be decoded@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"Ihash-of-failure\", @@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"go/dart-cbuild result: FAILURE\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-failure\\n\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }, @@@",
+      "@@@STEP_LOG_LINE@json.input@  \"revision_id\": \"current\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "rm",
+      "gs://dart-cbuild-test-results/hash-of-failure"
+    ],
+    "infra_step": true,
+    "name": "gsutil delete result for hash-of-failure"
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipes/dart/cbuild.expected/with-results.json b/recipes/dart/cbuild.expected/with-results.json
index 07b9680..229b5f7 100644
--- a/recipes/dart/cbuild.expected/with-results.json
+++ b/recipes/dart/cbuild.expected/with-results.json
@@ -23,13 +23,37 @@
       "----",
       "cp",
       "gs://dart-cbuild-test-results/hash-of-success",
-      "/path/to/tmp/"
+      "/path/to/tmp/json"
     ],
     "infra_step": true,
     "name": "gsutil download cbuild result for hash-of-success",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@raw_io.output_text[result]@true@@@",
-      "@@@STEP_LOG_END@raw_io.output_text[result]@@@"
+      "@@@STEP_LOG_LINE@json.output[result]@{@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"result\": true@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@}@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "cp",
+      "gs://dart-cbuild-test-results/hash-of-regression",
+      "/path/to/tmp/json"
+    ],
+    "infra_step": true,
+    "name": "gsutil download cbuild result for hash-of-regression",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output[result]@{@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"regression\": true, @@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"result\": false@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@}@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
     ]
   },
   {
@@ -42,13 +66,16 @@
       "----",
       "cp",
       "gs://dart-cbuild-test-results/hash-of-failure",
-      "/path/to/tmp/"
+      "/path/to/tmp/json"
     ],
     "infra_step": true,
     "name": "gsutil download cbuild result for hash-of-failure",
     "~followup_annotations": [
-      "@@@STEP_LOG_LINE@raw_io.output_text[result]@false@@@",
-      "@@@STEP_LOG_END@raw_io.output_text[result]@@@"
+      "@@@STEP_LOG_LINE@json.output[result]@{@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"regression\": false, @@@",
+      "@@@STEP_LOG_LINE@json.output[result]@  \"result\": false@@@",
+      "@@@STEP_LOG_LINE@json.output[result]@}@@@",
+      "@@@STEP_LOG_END@json.output[result]@@@"
     ]
   },
   {
@@ -60,7 +87,7 @@
     ],
     "infra_step": true,
     "name": "buildbucket.schedule",
-    "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-success\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"SUCCESS\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-success\"}, \"requestId\": \"0-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}, {\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-failure\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"FAILURE\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-failure\"}, \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+    "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-success\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"SUCCESS\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-success\"}, \"requestId\": \"0-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}, {\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-regression\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"FAILURE\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-regression\"}, \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}, {\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci.sandbox\", \"builder\": \"google\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"hash-of-failure\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"properties\": {\"result\": \"FAILURE\", \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-failure\"}, \"requestId\": \"0-00000000-0000-0000-0000-00000000133d\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
     "~followup_annotations": [
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"responses\": [@@@",
@@ -81,6 +108,15 @@
       "@@@STEP_LOG_LINE@json.output@        }, @@@",
       "@@@STEP_LOG_LINE@json.output@        \"id\": \"8922054662172514001\"@@@",
       "@@@STEP_LOG_LINE@json.output@      }@@@",
+      "@@@STEP_LOG_LINE@json.output@    }, @@@",
+      "@@@STEP_LOG_LINE@json.output@    {@@@",
+      "@@@STEP_LOG_LINE@json.output@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@json.output@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@json.output@        }, @@@",
+      "@@@STEP_LOG_LINE@json.output@        \"id\": \"8922054662172514002\"@@@",
+      "@@@STEP_LOG_LINE@json.output@      }@@@",
       "@@@STEP_LOG_LINE@json.output@    }@@@",
       "@@@STEP_LOG_LINE@json.output@  ]@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
@@ -124,6 +160,33 @@
       "@@@STEP_LOG_LINE@request@        \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
       "@@@STEP_LOG_LINE@request@        \"gitilesCommit\": {@@@",
       "@@@STEP_LOG_LINE@request@          \"host\": \"dart.googlesource.com\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"id\": \"hash-of-regression\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"project\": \"sdk\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"ref\": \"refs/heads/master\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"properties\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"result\": \"FAILURE\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-regression\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"tags\": [@@@",
+      "@@@STEP_LOG_LINE@request@          {@@@",
+      "@@@STEP_LOG_LINE@request@            \"key\": \"user_agent\", @@@",
+      "@@@STEP_LOG_LINE@request@            \"value\": \"recipe\"@@@",
+      "@@@STEP_LOG_LINE@request@          }@@@",
+      "@@@STEP_LOG_LINE@request@        ]@@@",
+      "@@@STEP_LOG_LINE@request@      }@@@",
+      "@@@STEP_LOG_LINE@request@    }, @@@",
+      "@@@STEP_LOG_LINE@request@    {@@@",
+      "@@@STEP_LOG_LINE@request@      \"scheduleBuild\": {@@@",
+      "@@@STEP_LOG_LINE@request@        \"builder\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"bucket\": \"ci.sandbox\", @@@",
+      "@@@STEP_LOG_LINE@request@          \"builder\": \"google\"@@@",
+      "@@@STEP_LOG_LINE@request@        }, @@@",
+      "@@@STEP_LOG_LINE@request@        \"experimental\": \"NO\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"gitilesCommit\": {@@@",
+      "@@@STEP_LOG_LINE@request@          \"host\": \"dart.googlesource.com\", @@@",
       "@@@STEP_LOG_LINE@request@          \"id\": \"hash-of-failure\", @@@",
       "@@@STEP_LOG_LINE@request@          \"project\": \"sdk\", @@@",
       "@@@STEP_LOG_LINE@request@          \"ref\": \"refs/heads/master\"@@@",
@@ -132,7 +195,7 @@
       "@@@STEP_LOG_LINE@request@          \"result\": \"FAILURE\", @@@",
       "@@@STEP_LOG_LINE@request@          \"url\": \"https://goto.google.com/dart-cbuild/find/hash-of-failure\"@@@",
       "@@@STEP_LOG_LINE@request@        }, @@@",
-      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-00000000133a\", @@@",
+      "@@@STEP_LOG_LINE@request@        \"requestId\": \"0-00000000-0000-0000-0000-00000000133d\", @@@",
       "@@@STEP_LOG_LINE@request@        \"tags\": [@@@",
       "@@@STEP_LOG_LINE@request@          {@@@",
       "@@@STEP_LOG_LINE@request@            \"key\": \"user_agent\", @@@",
@@ -145,7 +208,8 @@
       "@@@STEP_LOG_LINE@request@}@@@",
       "@@@STEP_LOG_END@request@@@",
       "@@@STEP_LINK@8922054662172514000@https://cr-buildbucket.appspot.com/build/8922054662172514000@@@",
-      "@@@STEP_LINK@8922054662172514001@https://cr-buildbucket.appspot.com/build/8922054662172514001@@@"
+      "@@@STEP_LINK@8922054662172514001@https://cr-buildbucket.appspot.com/build/8922054662172514001@@@",
+      "@@@STEP_LINK@8922054662172514002@https://cr-buildbucket.appspot.com/build/8922054662172514002@@@"
     ]
   },
   {
@@ -284,6 +348,82 @@
       "--json-file",
       "/path/to/tmp/json",
       "--url",
+      "https://dart.googlesource.com/sdk/+/hash-of-regression",
+      "--format",
+      "json"
+    ],
+    "name": "download change-id for hash-of-regression",
+    "~followup_annotations": [
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"author\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"commit\": \"7c6ab322bd26f369fb93303a1c866f79ec59a48d\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"committer\": {@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"email\": \"testauthor@fake.chromium.org\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"name\": \"Test Author\", @@@",
+      "@@@STEP_LOG_LINE@json.output@    \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  }, @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Subject\\n\\nChange-Id: Ihash-of-regression\\n\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"parents\": [@@@",
+      "@@@STEP_LOG_LINE@json.output@    \"f90914c0e844c152907d395449631c07d536a670\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  ], @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree\": \"ef3e32d5c0c7a44c7af721dd17ebf0ff671a4596\", @@@",
+      "@@@STEP_LOG_LINE@json.output@  \"tree_diff\": []@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "[CACHE]/cipd/infra/tools/luci/gerrit/latest/gerrit",
+      "set-review",
+      "-host",
+      "https://dart-review.googlesource.com",
+      "-input",
+      "{\"change_id\": \"Ihash-of-regression\", \"input\": {\"message\": \"go/dart-cbuild result: FAILURE (REGRESSIONS DETECTED)\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-regression\\n\"}, \"revision_id\": \"current\"}",
+      "-output",
+      "/path/to/tmp/json"
+    ],
+    "name": "update gerrit for hash-of-regression",
+    "~followup_annotations": [
+      "@@@STEP_LOG_END@json.output (invalid)@@@",
+      "@@@STEP_LOG_LINE@json.output (exception)@No JSON object could be decoded@@@",
+      "@@@STEP_LOG_END@json.output (exception)@@@",
+      "@@@STEP_LOG_LINE@json.input@{@@@",
+      "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"Ihash-of-regression\", @@@",
+      "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"go/dart-cbuild result: FAILURE (REGRESSIONS DETECTED)\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-regression\\n\"@@@",
+      "@@@STEP_LOG_LINE@json.input@  }, @@@",
+      "@@@STEP_LOG_LINE@json.input@  \"revision_id\": \"current\"@@@",
+      "@@@STEP_LOG_LINE@json.input@}@@@",
+      "@@@STEP_LOG_END@json.input@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "----",
+      "rm",
+      "gs://dart-cbuild-test-results/hash-of-regression"
+    ],
+    "infra_step": true,
+    "name": "gsutil delete result for hash-of-regression"
+  },
+  {
+    "cmd": [
+      "python",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gitiles]/resources/gerrit_client.py",
+      "--json-file",
+      "/path/to/tmp/json",
+      "--url",
       "https://dart.googlesource.com/sdk/+/hash-of-failure",
       "--format",
       "json"
@@ -319,7 +459,7 @@
       "-host",
       "https://dart-review.googlesource.com",
       "-input",
-      "{\"change_id\": \"Ihash-of-failure\", \"input\": {\"message\": \"go/dart-cbuild result: FAILURE\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-failure\\n\"}, \"revision_id\": \"current\"}",
+      "{\"change_id\": \"Ihash-of-failure\", \"input\": {\"message\": \"go/dart-cbuild result: FAILURE (NO REGRESSIONS DETECTED)\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-failure\\n\"}, \"revision_id\": \"current\"}",
       "-output",
       "/path/to/tmp/json"
     ],
@@ -331,7 +471,7 @@
       "@@@STEP_LOG_LINE@json.input@{@@@",
       "@@@STEP_LOG_LINE@json.input@  \"change_id\": \"Ihash-of-failure\", @@@",
       "@@@STEP_LOG_LINE@json.input@  \"input\": {@@@",
-      "@@@STEP_LOG_LINE@json.input@    \"message\": \"go/dart-cbuild result: FAILURE\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-failure\\n\"@@@",
+      "@@@STEP_LOG_LINE@json.input@    \"message\": \"go/dart-cbuild result: FAILURE (NO REGRESSIONS DETECTED)\\n\\nDetails: https://goto.google.com/dart-cbuild/find/hash-of-failure\\n\"@@@",
       "@@@STEP_LOG_LINE@json.input@  }, @@@",
       "@@@STEP_LOG_LINE@json.input@  \"revision_id\": \"current\"@@@",
       "@@@STEP_LOG_LINE@json.input@}@@@",
diff --git a/recipes/dart/cbuild.py b/recipes/dart/cbuild.py
index 0fe8965..dbe257f 100644
--- a/recipes/dart/cbuild.py
+++ b/recipes/dart/cbuild.py
@@ -23,6 +23,7 @@
     'depot_tools/gsutil',
     'fuchsia/gerrit',
     'recipe_engine/buildbucket',
+    'recipe_engine/json',
     'recipe_engine/properties',
     'recipe_engine/raw_io',
     'recipe_engine/runtime',
@@ -57,8 +58,8 @@
       commit_hash = result['commit_hash']
       change_id = _get_change_id(api, commit_hash)
       if not api.runtime.is_experimental and change_id:
-        message = CBUILD_MESSAGE % (common_pb2.Status.Name(
-            result['cbuild_result']), result['cbuild_url'])
+        message = CBUILD_MESSAGE % (result['cbuild_result_text'],
+                                    result['cbuild_url'])
         api.gerrit.set_review(
             'update gerrit for %s' % commit_hash,
             change_id,
@@ -83,16 +84,32 @@
     _, commit_hash = result_url.rsplit('/', 1)
     download = api.gsutil.download_url(
         result_url,
-        api.raw_io.output_text(name='result', add_output_log=True),
+        api.json.output(name='result'),
         name='download cbuild result for %s' % commit_hash)
-    cbuild_result_text = download.raw_io.output_texts.get('result').strip()
-    assert cbuild_result_text in ('true', 'false'), 'Unexpected cbuild result'
-    cbuild_success = cbuild_result_text == 'true'
+    cbuild_result_json = download.json.outputs['result']
+    cbuild_success = None
+    cbuild_regression = None
+    if cbuild_result_json in (True, False):
+      # TODO(athom): remove support for non-json legacy results.
+      cbuild_success = cbuild_result_json
+    else:
+      cbuild_success = cbuild_result_json['result']
+      cbuild_regression = cbuild_result_json.get('regression', False)
+
+    assert not (cbuild_success and cbuild_regression)
+
     cbuild_result = common_pb2.SUCCESS if cbuild_success else common_pb2.FAILURE
+    cbuild_result_text = common_pb2.Status.Name(cbuild_result)
+    if cbuild_regression:
+      cbuild_result_text += ' (REGRESSIONS DETECTED)'
+    elif not cbuild_success and cbuild_regression is not None:
+      cbuild_result_text += ' (NO REGRESSIONS DETECTED)'
     results.append({
         'result_url': result_url,
         'commit_hash': commit_hash,
         'cbuild_result': cbuild_result,
+        'cbuild_result_text': cbuild_result_text,
+        'cbuild_regression': cbuild_regression,
         'cbuild_url': CBUILD_RESULT_URL % commit_hash,
     })
   return results
@@ -139,7 +156,7 @@
   def _cbuild_results(commit_hash, cbuild_result):
     return sum([
         api.step_data('gsutil download cbuild result for %s' % commit_hash,
-                      api.raw_io.output_text(cbuild_result, name='result')),
+                      api.json.output(cbuild_result, name='result')),
         api.step_data(
             'download change-id for %s' % commit_hash,
             api.gitiles.make_commit_test_data(
@@ -148,6 +165,7 @@
 
   CBUILD_RESULT_URLS = '''gs://dart-cbuild-test-results/hash-of-success
 gs://dart-cbuild-test-results/ignore-this-directory/
+gs://dart-cbuild-test-results/hash-of-regression
 gs://dart-cbuild-test-results/hash-of-failure
 '''
   yield api.test(
@@ -155,15 +173,49 @@
       api.step_data(
           'gsutil find cbuild results',
           stdout=api.raw_io.output(CBUILD_RESULT_URLS)),
-      _cbuild_results('hash-of-success', 'true'),
-      _cbuild_results('hash-of-failure', 'false'),
+      _cbuild_results('hash-of-success', {'result': True}),
+      _cbuild_results('hash-of-regression', {
+          'result': False,
+          'regression': True
+      }),
+      _cbuild_results('hash-of-failure', {
+          'result': False,
+          'regression': False
+      }),
       api.post_process(post_process.MustRun,
                        'update gerrit for hash-of-success'),
       api.post_process(post_process.MustRun,
+                       'update gerrit for hash-of-regression'),
+      api.post_process(post_process.MustRun,
                        'update gerrit for hash-of-failure'),
       api.post_process(post_process.MustRun,
                        'gsutil delete result for hash-of-success'),
       api.post_process(post_process.MustRun,
+                       'gsutil delete result for hash-of-regression'),
+      api.post_process(post_process.MustRun,
+                       'gsutil delete result for hash-of-failure'),
+      api.post_process(post_process.StatusSuccess),
+  )
+
+  yield api.test(
+      'with-legacy-results',
+      api.step_data(
+          'gsutil find cbuild results',
+          stdout=api.raw_io.output(CBUILD_RESULT_URLS)),
+      _cbuild_results('hash-of-success', True),
+      _cbuild_results('hash-of-regression', False),
+      _cbuild_results('hash-of-failure', False),
+      api.post_process(post_process.MustRun,
+                       'update gerrit for hash-of-success'),
+      api.post_process(post_process.MustRun,
+                       'update gerrit for hash-of-regression'),
+      api.post_process(post_process.MustRun,
+                       'update gerrit for hash-of-failure'),
+      api.post_process(post_process.MustRun,
+                       'gsutil delete result for hash-of-success'),
+      api.post_process(post_process.MustRun,
+                       'gsutil delete result for hash-of-regression'),
+      api.post_process(post_process.MustRun,
                        'gsutil delete result for hash-of-failure'),
       api.post_process(post_process.StatusSuccess),
   )
@@ -174,8 +226,15 @@
       api.step_data(
           'gsutil find cbuild results',
           stdout=api.raw_io.output(CBUILD_RESULT_URLS)),
-      _cbuild_results('hash-of-success', 'true'),
-      _cbuild_results('hash-of-failure', 'false'),
+      _cbuild_results('hash-of-success', {'result': True}),
+      _cbuild_results('hash-of-regression', {
+          'result': False,
+          'regression': True
+      }),
+      _cbuild_results('hash-of-failure', {
+          'result': False,
+          'regression': False
+      }),
       api.post_process(post_process.DoesNotRunRE, '.*(delete|update).*'),
       api.post_process(post_process.StatusSuccess),
   )
@@ -186,7 +245,7 @@
           'gsutil find cbuild results',
           stdout=api.raw_io.output('gs://dart-cbuild-test-results/hash')),
       api.step_data('gsutil download cbuild result for hash',
-                    api.raw_io.output_text('false', name='result')),
+                    api.json.output(False, name='result')),
       api.step_data('download change-id for hash',
                     api.gitiles.make_commit_test_data('hash', 'Subject\n')),
       api.post_process(post_process.DoesNotRunRE, '.*update gerrit.*'),
diff --git a/recipes/roller/roll_to_dev.py b/recipes/roller/roll_to_dev.py
index 1594401..88de01a 100644
--- a/recipes/roller/roll_to_dev.py
+++ b/recipes/roller/roll_to_dev.py
@@ -13,10 +13,8 @@
     'depot_tools/gitiles',
     'recipe_engine/context',
     'recipe_engine/file',
-    'recipe_engine/json',
     'recipe_engine/path',
     'recipe_engine/properties',
-    'recipe_engine/raw_io',
     'recipe_engine/runtime',
     'recipe_engine/step',
     'recipe_engine/time',