[tree_status] Support using new tree status app.

This causes the tree-closer to update both the old and new tree status
apps, but uses the old status app as the source of truth for getting the
current tree status. Once the tree_name property is provided, the
tree-closer will start using the new app as the source of truth, but
until the Gerrit plugin and CV are migrated to using the new status app,
we'll have to keep both in sync, so any manually set statuses in either
app will be copied over to the other if detected by the tree-closer, and
the auto-submit builder will check both and take the latest status.

Bug: 332741591
Change-Id: Icdf7f72a2b8ab933d99d5aa2336722e3bc8bf128
Reviewed-on: https://fuchsia-review.googlesource.com/c/infra/recipes/+/1041743
Reviewed-by: Oliver Newman <olivernewman@google.com>
Fuchsia-Auto-Submit: Ina Huh <ihuh@google.com>
Commit-Queue: Ina Huh <ihuh@google.com>
diff --git a/recipe_modules/gerrit_auto_submit/api.py b/recipe_modules/gerrit_auto_submit/api.py
index d3e2ea5..9b7698d 100644
--- a/recipe_modules/gerrit_auto_submit/api.py
+++ b/recipe_modules/gerrit_auto_submit/api.py
@@ -290,10 +290,15 @@
         dry_run,
         state,
         results,
+        tree_name=None,
     ):
         tree_status = None
-        if tree_status_host:
+        if tree_status_host or tree_name:
             tree_status = self.m.tree_status.get(tree_status_host)
+            if tree_name:
+                new_status = self.m.tree_status.get("", tree_name=tree_name)
+                if new_status.date > tree_status.date:
+                    tree_status = new_status
 
         changes = None
         with self.m.step.nest("get eligible") as presentation:
@@ -398,6 +403,7 @@
                         dry_run,
                         host_state,
                         results,
+                        tree_name=host_config.tree_name,
                     )
                 )
                 state[gerrit_host] = host_state.copy()
diff --git a/recipe_modules/gerrit_auto_submit/options.proto b/recipe_modules/gerrit_auto_submit/options.proto
index a82c85b..3fa1b13 100644
--- a/recipe_modules/gerrit_auto_submit/options.proto
+++ b/recipe_modules/gerrit_auto_submit/options.proto
@@ -14,6 +14,9 @@
 
   // Hostname of tree status site.
   string tree_status_host = 3;
+
+  // Name of the tree to get the status for.
+  string tree_name = 4;
 }
 
 message Options {
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/changes_submitted_together.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/changes_submitted_together.json
index 8198cc6..8cb1349 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/changes_submitted_together.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/changes_submitted_together.json
@@ -45,11 +45,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/dry_run_multiple.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/dry_run_multiple.json
index 52cd77a..876486b 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/dry_run_multiple.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/dry_run_multiple.json
@@ -50,11 +50,12 @@
     "name": "fuchsia.get current tree status",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -289,11 +290,12 @@
     "name": "test.get current tree status",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/multiple_iterations.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/multiple_iterations.json
index cc73b1b..4fd7dc8 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/multiple_iterations.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/multiple_iterations.json
@@ -53,11 +53,12 @@
     "name": "0.get current tree status",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -359,11 +360,12 @@
     "name": "1.get current tree status",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -517,11 +519,12 @@
     "name": "2.get current tree status",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -578,11 +581,12 @@
     "name": "3.get current tree status",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -639,11 +643,12 @@
     "name": "4.get current tree status",
     "~followup_annotations": [
       "@@@STEP_NEST_LEVEL@1@@@",
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/no_changes.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/no_changes.json
index a90cdf6..afc3782 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/no_changes.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/no_changes.json
@@ -45,11 +45,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/state_retryable.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/state_retryable.json
index 9d2b7ad..43531cd 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/state_retryable.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/state_retryable.json
@@ -106,11 +106,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/state_too_many_attempts.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/state_too_many_attempts.json
index 288e636..393c38a 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/state_too_many_attempts.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/state_too_many_attempts.json
@@ -106,11 +106,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/state_too_many_attempts_comments.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/state_too_many_attempts_comments.json
index c37b7af..12c5d07 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/state_too_many_attempts_comments.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/state_too_many_attempts_comments.json
@@ -45,11 +45,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/state_tree_closed.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/state_tree_closed.json
index 1acc9d9..367bfb5 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/state_tree_closed.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/state_tree_closed.json
@@ -106,11 +106,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@closed@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"closed\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -118,6 +119,28 @@
     ]
   },
   {
+    "cmd": [
+      "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
+      "",
+      "--tree-name",
+      "fuchsia",
+      "--use-new-status-app",
+      "get"
+    ],
+    "name": "get current tree status (2)",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@CLOSED@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"createTime\": \"2020-12-01T23:07:11.234Z\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"generalState\": \"CLOSED\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is CLOSED\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"name\": \"trees/fuchsia/status/12345\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@fuchsia@https://ci.chromium.org/ui/labs/tree-status/fuchsia@@@"
+    ]
+  },
+  {
     "cmd": [],
     "name": "get eligible"
   },
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/unmergeable.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/unmergeable.json
index 6736077..503a1e3 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/unmergeable.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/unmergeable.json
@@ -45,11 +45,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.expected/unmergeable_nontrivial_merge_conflict.json b/recipe_modules/gerrit_auto_submit/tests/full.expected/unmergeable_nontrivial_merge_conflict.json
index 64da8c9..a04a0a5 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.expected/unmergeable_nontrivial_merge_conflict.json
+++ b/recipe_modules/gerrit_auto_submit/tests/full.expected/unmergeable_nontrivial_merge_conflict.json
@@ -45,11 +45,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/gerrit_auto_submit/tests/full.py b/recipe_modules/gerrit_auto_submit/tests/full.py
index 342e2b9..e066905 100644
--- a/recipe_modules/gerrit_auto_submit/tests/full.py
+++ b/recipe_modules/gerrit_auto_submit/tests/full.py
@@ -72,9 +72,14 @@
     # If the tree is closed we should wait to retry changes.
     yield (
         api.test("state_tree_closed")
-        + api.gerrit_auto_submit.options()
+        + api.gerrit_auto_submit.options(
+            api.gerrit_auto_submit.host_config(tree_name="fuchsia")
+        )
         + api.builder_state(previous_state_retryable)
-        + api.tree_status.get(state="closed")
+        + api.tree_status.get(state="OPEN")
+        + api.tree_status.get(
+            state="CLOSED", tree_name="fuchsia", step_name="get current tree status (2)"
+        )
         + api.gerrit_auto_submit.changes_query_test_data()
         + api.gerrit_auto_submit.changes_submitted_together_test_data()
     )
diff --git a/recipe_modules/tree_status/api.py b/recipe_modules/tree_status/api.py
index 8bb43a4..505742e 100644
--- a/recipe_modules/tree_status/api.py
+++ b/recipe_modules/tree_status/api.py
@@ -10,32 +10,46 @@
 
 # The format used for the "date" field in HTTP responses from requests to the
 # tree status page.
-TREE_STATUS_DATE_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
+OLD_TREE_STATUS_DATE_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
+TREE_STATUS_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
 
 
 class State(enum.Enum):
-    OPEN = "open"
-    CLOSED = "closed"
-    THROTTLED = "throttled"
-    MAINTENANCE = "maintenance"
+    OPEN = "OPEN"
+    CLOSED = "CLOSED"
+    THROTTLED = "THROTTLED"
+    MAINTENANCE = "MAINTENANCE"
 
 
 @attr.s
 class TreeStatus:
     state = attr.ib(type=str)
     date = attr.ib(type=datetime.datetime)
-    username = attr.ib(type=str)
     # Unique ID of this status.
-    key = attr.ib(type=int)
+    key = attr.ib(type=str)
+    message = attr.ib(type=str)
+    username = attr.ib(type=str, default=None)
 
     @classmethod
-    def from_dict(cls, general_state, date, username, key, **_):
+    def from_dict(cls, name, generalState, createTime, message, **_):
         """Create a TreeStatus from a dict, ignoring any unneeded values."""
         return cls(
-            state=State(general_state),
-            date=datetime.datetime.strptime(date, TREE_STATUS_DATE_FORMAT),
+            state=State(generalState),
+            date=datetime.datetime.strptime(createTime, TREE_STATUS_DATE_FORMAT),
+            message=message,
+            # The `name` will be in the form of `trees/{tree_name}/status/{key}`.
+            key=name.split("/")[-1].strip(),
+        )
+
+    @classmethod
+    def old_status_from_dict(cls, general_state, date, username, key, message, **_):
+        """Create a TreeStatus from a dict, ignoring any unneeded values."""
+        return cls(
+            state=State(general_state.upper()),
+            date=datetime.datetime.strptime(date, OLD_TREE_STATUS_DATE_FORMAT),
             username=username,
-            key=key,
+            key=str(key),
+            message=message,
         )
 
     @property
@@ -48,7 +62,7 @@
     def _tree_status_script(self):
         return self.resource("tree_status.py")
 
-    def get(self, hostname, step_name="get current tree status"):
+    def get(self, hostname, tree_name=None, step_name="get current tree status"):
         """Get the current tree status.
 
         Args:
@@ -57,15 +71,30 @@
         Returns:
             A `TreeStatus` corresponding to the current state of the tree.
         """
+        cmd = [
+            self._tree_status_script,
+            hostname,
+        ]
+        if tree_name:
+            cmd.extend(["--tree-name", tree_name, "--use-new-status-app"])
+        cmd.append("get")
         step = self.m.step(
             step_name,
-            [self._tree_status_script, hostname, "get"],
+            cmd,
             stdout=self.m.json.output(),
             step_test_data=self.test_api.status_step_data,
         )
-        status = TreeStatus.from_dict(**step.stdout)
+        if tree_name:
+            status = TreeStatus.from_dict(**step.stdout)
+        else:
+            status = TreeStatus.old_status_from_dict(**step.stdout)
         step.presentation.step_text = status.state.value
-        step.presentation.links[hostname] = f"https://{hostname}"
+        if tree_name:
+            step.presentation.links[tree_name] = (
+                f"https://ci.chromium.org/ui/labs/tree-status/{tree_name}"
+            )
+        else:
+            step.presentation.links[hostname] = f"https://{hostname}"
         return status
 
     def update(
@@ -73,6 +102,7 @@
         hostname,
         admin_hostname,
         message,
+        tree_name=None,
         username=None,
         state=None,
         issuetracker_id=None,
@@ -97,7 +127,9 @@
         # https://chromium.googlesource.com/infra/infra/+/09d53b324dd786b96d69c6cbb8ee6c389b22fd3f/appengine/chromium_status/appengine_module/chromium_status/status.py#426
         if last_status:
             current_status = self.get(
-                hostname, step_name="check for tree status collision"
+                hostname,
+                tree_name=tree_name,
+                step_name="check for tree status collision",
             )
             if current_status.key != last_status.key:
                 raise self.m.step.StepFailure(
@@ -106,13 +138,19 @@
         cmd = [
             self._tree_status_script,
             hostname,
-            "set",
-            message,
-            "--admin_hostname",
-            admin_hostname,
-            "--username",
-            username or self.m.buildbucket.builder_name,
         ]
+        if tree_name:
+            cmd.extend(["--tree-name", tree_name, "--use-new-status-app"])
+        cmd.extend(
+            [
+                "set",
+                message,
+                "--admin_hostname",
+                admin_hostname,
+                "--username",
+                username or self.m.buildbucket.builder_name,
+            ]
+        )
         if state:
             cmd.extend(["--state", state])
         if issuetracker_id:
diff --git a/recipe_modules/tree_status/resources/tree_status.py b/recipe_modules/tree_status/resources/tree_status.py
index 9d939a8..a1d6a11 100755
--- a/recipe_modules/tree_status/resources/tree_status.py
+++ b/recipe_modules/tree_status/resources/tree_status.py
@@ -73,10 +73,26 @@
 import urllib.parse
 import warnings
 
+JSON_PREFIX = ")]}'"
+
+TREE_STATUS_SERVICE = (
+    "https://luci-tree-status.appspot.com/prpc/luci.tree_status.v1.TreeStatus"
+)
+
 
 def main():
     parser = argparse.ArgumentParser(description="Get and set tree status.")
     parser.add_argument("hostname", help="Hostname for the status page")
+    parser.add_argument(
+        "--tree-name",
+        default="fuchsia-stem",
+        help="Name of the tree to get the status for",
+    )
+    parser.add_argument(
+        "--use-new-status-app",
+        action="store_true",
+        help="Whether to use the new tree status app",
+    )
 
     subparsers = parser.add_subparsers(help="Subcommands", required=True)
 
@@ -109,7 +125,13 @@
     sleep_seconds = 1
     for _ in range(max_attempts):
         try:
-            result = make_request(args.hostname, "/current?format=json")
+            result = make_googleplex_request(
+                f"{TREE_STATUS_SERVICE}/GetStatus",
+                {"name": f"trees/{args.tree_name}/status/latest"},
+            )
+            if not args.use_new_status_app:
+                result = make_request(args.hostname, "/current?format=json")
+
         except BadHTTPResponse as e:
             if e.status < 500:
                 raise
@@ -124,18 +146,30 @@
 
 def set_status(args):
     try:
-        # This will print the UUID of the created status.
-        print(
-            make_googleplex_request(
-                f"https://{args.admin_hostname}/api/v1/status",
-                {
-                    "user": args.username,
+        # TODO(https://fxbug.dev/332741591): We can stop setting the status in
+        # both apps once we no longer need the old app for the stats dashboards.
+        result = make_googleplex_request(
+            f"{TREE_STATUS_SERVICE}/CreateStatus",
+            {
+                "parent": f"trees/{args.tree_name}/status",
+                "status": {
+                    "generalState": args.state.upper(),
                     "message": args.message,
-                    "state": args.state,
-                    "issue_tracker_id": args.issuetracker_id,
                 },
-            )
+            },
         )
+        old_app_result = make_googleplex_request(
+            f"https://{args.admin_hostname}/api/v1/status",
+            {
+                "user": args.username,
+                "message": args.message,
+                "state": args.state,
+                "issue_tracker_id": args.issuetracker_id,
+            },
+        )
+        if not args.use_new_status_app:
+            result = old_app_result
+        print(result)
     except Exception as e:
         warnings.warn(str(e))
 
@@ -174,6 +208,7 @@
     resp = requests.post(
         url,
         headers={
+            "Accept": "application/json",
             "Authorization": "Bearer " + tokenResp.json()["access_token"],
             "Content-Type": "application/json",
         },
@@ -182,10 +217,14 @@
         # Disallow them to make it clear when there is a 403 Unauthorized.
         allow_redirects=False,
     )
-    if resp.status >= 500:
+    if resp.status_code >= 500:
         raise Exception(f"bad response status {int(resp.status)}: {resp.text}")
 
-    return resp.text
+    result = resp.text
+    if result.startswith(JSON_PREFIX):
+        result = result[len(JSON_PREFIX) :]
+
+    return result
 
 
 if __name__ == "__main__":
diff --git a/recipe_modules/tree_status/test_api.py b/recipe_modules/tree_status/test_api.py
index a33ce35..8c0c6a8 100644
--- a/recipe_modules/tree_status/test_api.py
+++ b/recipe_modules/tree_status/test_api.py
@@ -10,12 +10,22 @@
         step_name = kwargs.pop("step_name", "get current tree status")
         return self.step_data(step_name, self.status_step_data(**kwargs))
 
-    def status_step_data(self, state="open", key=12345):
+    def status_step_data(self, state="OPEN", key=12345, tree_name=None):
+        if tree_name:
+            return self.m.json.output_stream(
+                {
+                    "generalState": state,
+                    "createTime": "2020-12-01T23:07:11.234Z",
+                    "name": f"trees/{tree_name}/status/{key}",
+                    "message": f"Tree is {state}",
+                }
+            )
         return self.m.json.output_stream(
             {
-                "general_state": state,
+                "general_state": state.lower(),
                 "date": "2020-12-01 23:07:06.234",
                 "username": "user@example.com",
                 "key": key,
+                "message": f"Tree is {state}",
             }
         )
diff --git a/recipe_modules/tree_status/tests/full.expected/closed.json b/recipe_modules/tree_status/tests/full.expected/closed.json
index 9633d51..cb6d57d 100644
--- a/recipe_modules/tree_status/tests/full.expected/closed.json
+++ b/recipe_modules/tree_status/tests/full.expected/closed.json
@@ -3,25 +3,31 @@
     "cmd": [
       "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
       "fuchsia-stem-status.appspot.com",
+      "--tree-name",
+      "fuchsia",
+      "--use-new-status-app",
       "get"
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@closed@@@",
+      "@@@STEP_TEXT@CLOSED@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"closed\",@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"createTime\": \"2020-12-01T23:07:11.234Z\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"generalState\": \"CLOSED\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is CLOSED\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"name\": \"trees/fuchsia/status/12345\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@fuchsia-stem-status.appspot.com@https://fuchsia-stem-status.appspot.com@@@"
+      "@@@STEP_LINK@fuchsia@https://ci.chromium.org/ui/labs/tree-status/fuchsia@@@"
     ]
   },
   {
     "cmd": [
       "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
       "fuchsia-stem-status.appspot.com",
+      "--tree-name",
+      "fuchsia",
+      "--use-new-status-app",
       "set",
       "Tree is closed.",
       "--admin_hostname",
diff --git a/recipe_modules/tree_status/tests/full.expected/collision.json b/recipe_modules/tree_status/tests/full.expected/collision.json
index 6e70f3e..a058fbc 100644
--- a/recipe_modules/tree_status/tests/full.expected/collision.json
+++ b/recipe_modules/tree_status/tests/full.expected/collision.json
@@ -7,11 +7,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 123,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -26,11 +27,12 @@
     ],
     "name": "check for tree status collision",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 124,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipe_modules/tree_status/tests/full.py b/recipe_modules/tree_status/tests/full.py
index d55658b..133ced7 100644
--- a/recipe_modules/tree_status/tests/full.py
+++ b/recipe_modules/tree_status/tests/full.py
@@ -14,11 +14,12 @@
 
 PROPERTIES = {
     "check_for_collisions": Property(default=False),
+    "tree_name": Property(default=None),
 }
 
 
-def RunSteps(api, check_for_collisions):
-    status = api.tree_status.get(hostname=HOSTNAME)
+def RunSteps(api, check_for_collisions, tree_name):
+    status = api.tree_status.get(hostname=HOSTNAME, tree_name=tree_name)
     status.open  # pylint: disable=pointless-statement
 
     last_status = None
@@ -32,11 +33,14 @@
         last_status=last_status,
         state="CLOSED",
         issuetracker_id="1337",
+        tree_name=tree_name,
     )
 
 
 def GenTests(api):
-    yield api.test("closed") + api.tree_status.get(state="closed")
+    yield api.test("closed") + api.properties(
+        tree_name="fuchsia"
+    ) + api.tree_status.get(state="CLOSED", tree_name="fuchsia")
     yield (
         api.test("collision", status="FAILURE")
         + api.properties(check_for_collisions=True)
diff --git a/recipes/gerrit_auto_submit.expected/default.json b/recipes/gerrit_auto_submit.expected/default.json
index fe4e1a2..3521b8e 100644
--- a/recipes/gerrit_auto_submit.expected/default.json
+++ b/recipes/gerrit_auto_submit.expected/default.json
@@ -45,11 +45,12 @@
     ],
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/all_green.json b/recipes/tree_closer.expected/all_green.json
index c881ec1..7fa8830 100644
--- a/recipes/tree_closer.expected/all_green.json
+++ b/recipes/tree_closer.expected/all_green.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/concurrent_failures.json b/recipes/tree_closer.expected/concurrent_failures.json
index 8acb263..3d57ab3 100644
--- a/recipes/tree_closer.expected/concurrent_failures.json
+++ b/recipes/tree_closer.expected/concurrent_failures.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -716,11 +717,12 @@
     },
     "name": "check for tree status collision",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -732,7 +734,7 @@
       "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
       "example.com",
       "set",
-      "Tree is closed: https://fxbug.dev/605",
+      "Tree is closed: https://fxbug.dev/605 (automatic)",
       "--admin_hostname",
       "admin.example.com",
       "--username",
diff --git a/recipes/tree_closer.expected/concurrent_failures_no_matched_step.json b/recipes/tree_closer.expected/concurrent_failures_no_matched_step.json
index 6dab280..fe00ff3 100644
--- a/recipes/tree_closer.expected/concurrent_failures_no_matched_step.json
+++ b/recipes/tree_closer.expected/concurrent_failures_no_matched_step.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/consecutive_failures.json b/recipes/tree_closer.expected/consecutive_failures.json
index 0c604a5..118f03b 100644
--- a/recipes/tree_closer.expected/consecutive_failures.json
+++ b/recipes/tree_closer.expected/consecutive_failures.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -58,6 +59,107 @@
     ]
   },
   {
+    "cmd": [
+      "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
+      "",
+      "--tree-name",
+      "fuchsia-stem",
+      "--use-new-status-app",
+      "get"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "fuchsia:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get current tree status (2)",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@CLOSED@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"createTime\": \"2020-06-20T09:58:55.000000Z\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"generalState\": \"CLOSED\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is CLOSED (automatic)\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"name\": \"trees/fuchsia-stem/status/12345\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@fuchsia-stem@https://ci.chromium.org/ui/labs/tree-status/fuchsia-stem@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
+      "example.com",
+      "--tree-name",
+      "fuchsia-stem",
+      "--use-new-status-app",
+      "get"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "fuchsia:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "check for tree status collision",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@CLOSED@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"createTime\": \"2020-06-20T09:58:55.000000Z\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"generalState\": \"CLOSED\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is CLOSED (automatic)\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"name\": \"trees/fuchsia-stem/status/12345\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@fuchsia-stem@https://ci.chromium.org/ui/labs/tree-status/fuchsia-stem@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
+      "example.com",
+      "--tree-name",
+      "fuchsia-stem",
+      "--use-new-status-app",
+      "set",
+      "Tree is OPEN (automatic)",
+      "--admin_hostname",
+      "admin.example.com",
+      "--username",
+      "auto-closer",
+      "--state",
+      "OPEN"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "fuchsia:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "copy tree status to new app"
+  },
+  {
     "cmd": [],
     "name": "fetch fuchsia luci-milo.cfg"
   },
@@ -677,6 +779,9 @@
     "cmd": [
       "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
       "example.com",
+      "--tree-name",
+      "fuchsia-stem",
+      "--use-new-status-app",
       "get"
     ],
     "infra_step": true,
@@ -692,25 +797,28 @@
         "hostname": "rdbhost"
       }
     },
-    "name": "check for tree status collision",
+    "name": "check for tree status collision (2)",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"createTime\": \"2020-06-20T09:58:55.000000Z\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"generalState\": \"OPEN\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"name\": \"trees/fuchsia-stem/status/12345\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
-      "@@@STEP_LINK@example.com@https://example.com@@@"
+      "@@@STEP_LINK@fuchsia-stem@https://ci.chromium.org/ui/labs/tree-status/fuchsia-stem@@@"
     ]
   },
   {
     "cmd": [
       "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
       "example.com",
+      "--tree-name",
+      "fuchsia-stem",
+      "--use-new-status-app",
       "set",
-      "Tree is closed: https://fxbug.dev/605",
+      "Tree is closed: https://fxbug.dev/605 (automatic)",
       "--admin_hostname",
       "admin.example.com",
       "--username",
diff --git a/recipes/tree_closer.expected/consecutive_failures_no_matched_step.json b/recipes/tree_closer.expected/consecutive_failures_no_matched_step.json
index 8b41b4e..56fb640 100644
--- a/recipes/tree_closer.expected/consecutive_failures_no_matched_step.json
+++ b/recipes/tree_closer.expected/consecutive_failures_no_matched_step.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/disable_dates.json b/recipes/tree_closer.expected/disable_dates.json
index e439555..55db0d9 100644
--- a/recipes/tree_closer.expected/disable_dates.json
+++ b/recipes/tree_closer.expected/disable_dates.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/ignored_consecutive_failures.json b/recipes/tree_closer.expected/ignored_consecutive_failures.json
index 631aed1..f62957e 100644
--- a/recipes/tree_closer.expected/ignored_consecutive_failures.json
+++ b/recipes/tree_closer.expected/ignored_consecutive_failures.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/nighttime.json b/recipes/tree_closer.expected/nighttime.json
index 1e3e628..2772b4f 100644
--- a/recipes/tree_closer.expected/nighttime.json
+++ b/recipes/tree_closer.expected/nighttime.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/nighttime_reopen_tree.json b/recipes/tree_closer.expected/nighttime_reopen_tree.json
index 20aa416..603a3f1 100644
--- a/recipes/tree_closer.expected/nighttime_reopen_tree.json
+++ b/recipes/tree_closer.expected/nighttime_reopen_tree.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@closed@@@",
+      "@@@STEP_TEXT@CLOSED@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"closed\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is CLOSED (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"auto-closer\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -291,11 +292,12 @@
     },
     "name": "check for tree status collision",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -307,7 +309,7 @@
       "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
       "example.com",
       "set",
-      "Tree is open",
+      "Tree is open (automatic)",
       "--admin_hostname",
       "admin.example.com",
       "--username",
diff --git a/recipes/tree_closer.expected/not_enough_history.json b/recipes/tree_closer.expected/not_enough_history.json
index ebd3930..ca2fdbd 100644
--- a/recipes/tree_closer.expected/not_enough_history.json
+++ b/recipes/tree_closer.expected/not_enough_history.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/reopen.json b/recipes/tree_closer.expected/reopen.json
index eb8dcc4..a625d2d 100644
--- a/recipes/tree_closer.expected/reopen.json
+++ b/recipes/tree_closer.expected/reopen.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@closed@@@",
+      "@@@STEP_TEXT@CLOSED@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"closed\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is CLOSED (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"auto-closer\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -291,11 +292,12 @@
     },
     "name": "check for tree status collision",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -307,7 +309,7 @@
       "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
       "example.com",
       "set",
-      "Tree is open",
+      "Tree is open (automatic)",
       "--admin_hostname",
       "admin.example.com",
       "--username",
diff --git a/recipes/tree_closer.expected/summary_regexp_ignore.json b/recipes/tree_closer.expected/summary_regexp_ignore.json
index 25ae8fc..d9f0451 100644
--- a/recipes/tree_closer.expected/summary_regexp_ignore.json
+++ b/recipes/tree_closer.expected/summary_regexp_ignore.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/throttled__no_history.json b/recipes/tree_closer.expected/throttled__no_history.json
index b9468bc..a8672d2 100644
--- a/recipes/tree_closer.expected/throttled__no_history.json
+++ b/recipes/tree_closer.expected/throttled__no_history.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@throttled@@@",
+      "@@@STEP_TEXT@THROTTLED@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"throttled\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is THROTTLED (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/tree_already_closed.json b/recipes/tree_closer.expected/tree_already_closed.json
index 115bfa0..47a1de7 100644
--- a/recipes/tree_closer.expected/tree_already_closed.json
+++ b/recipes/tree_closer.expected/tree_already_closed.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@closed@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
-      "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"closed\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"auto-closer\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
@@ -58,6 +59,102 @@
     ]
   },
   {
+    "cmd": [
+      "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
+      "",
+      "--tree-name",
+      "fuchsia",
+      "--use-new-status-app",
+      "get"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "fuchsia:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "get current tree status (2)",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@CLOSED@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"createTime\": \"2020-06-20T09:59:05.000000Z\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"generalState\": \"CLOSED\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is CLOSED (automatic)\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"name\": \"trees/fuchsia/status/12345\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@fuchsia@https://ci.chromium.org/ui/labs/tree-status/fuchsia@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
+      "example.com",
+      "get"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "fuchsia:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "check for tree status collision",
+    "~followup_annotations": [
+      "@@@STEP_TEXT@OPEN@@@",
+      "@@@STEP_LOG_LINE@json.output@{@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"username\": \"auto-closer\"@@@",
+      "@@@STEP_LOG_LINE@json.output@}@@@",
+      "@@@STEP_LOG_END@json.output@@@",
+      "@@@STEP_LINK@example.com@https://example.com@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "RECIPE_MODULE[fuchsia::tree_status]/resources/tree_status.py",
+      "example.com",
+      "set",
+      "Tree is CLOSED (automatic)",
+      "--admin_hostname",
+      "admin.example.com",
+      "--username",
+      "auto-closer",
+      "--state",
+      "CLOSED"
+    ],
+    "infra_step": true,
+    "luci_context": {
+      "realm": {
+        "name": "fuchsia:ci"
+      },
+      "resultdb": {
+        "current_invocation": {
+          "name": "invocations/build:8945511751514863184",
+          "update_token": "token"
+        },
+        "hostname": "rdbhost"
+      }
+    },
+    "name": "copy tree status to old app"
+  },
+  {
     "cmd": [],
     "name": "fetch fuchsia luci-milo.cfg"
   },
diff --git a/recipes/tree_closer.expected/tree_closed_by_human.json b/recipes/tree_closer.expected/tree_closed_by_human.json
index 8430461..552bb69 100644
--- a/recipes/tree_closer.expected/tree_closed_by_human.json
+++ b/recipes/tree_closer.expected/tree_closed_by_human.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@closed@@@",
+      "@@@STEP_TEXT@CLOSED@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 09:59:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"closed\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is CLOSED (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/tree_recently_opened.json b/recipes/tree_closer.expected/tree_recently_opened.json
index c1a4ba3..ff79a84 100644
--- a/recipes/tree_closer.expected/tree_recently_opened.json
+++ b/recipes/tree_closer.expected/tree_recently_opened.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-06-20 10:01:00.000000\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN (automatic)\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"human@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.expected/weekend.json b/recipes/tree_closer.expected/weekend.json
index 6f33d93..2400de1 100644
--- a/recipes/tree_closer.expected/weekend.json
+++ b/recipes/tree_closer.expected/weekend.json
@@ -46,11 +46,12 @@
     },
     "name": "get current tree status",
     "~followup_annotations": [
-      "@@@STEP_TEXT@open@@@",
+      "@@@STEP_TEXT@OPEN@@@",
       "@@@STEP_LOG_LINE@json.output@{@@@",
       "@@@STEP_LOG_LINE@json.output@  \"date\": \"2020-12-01 23:07:06.234\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"general_state\": \"open\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"key\": 12345,@@@",
+      "@@@STEP_LOG_LINE@json.output@  \"message\": \"Tree is OPEN\",@@@",
       "@@@STEP_LOG_LINE@json.output@  \"username\": \"user@example.com\"@@@",
       "@@@STEP_LOG_LINE@json.output@}@@@",
       "@@@STEP_LOG_END@json.output@@@",
diff --git a/recipes/tree_closer.proto b/recipes/tree_closer.proto
index 10af59f..e960cfb 100644
--- a/recipes/tree_closer.proto
+++ b/recipes/tree_closer.proto
@@ -53,4 +53,7 @@
 
   // Component id to set for the IssueTracker tree closure bug.
   int32 bug_component_id = 9;
+
+  // The name of the tree to get/set the status for.
+  string tree_name = 10;
 }
diff --git a/recipes/tree_closer.py b/recipes/tree_closer.py
index 220297e..5476020 100644
--- a/recipes/tree_closer.py
+++ b/recipes/tree_closer.py
@@ -43,7 +43,10 @@
 
 # The format used for the "date" field in HTTP responses from requests to the
 # tree status page.
-TREE_STATUS_DATE_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
+OLD_TREE_STATUS_DATE_FORMAT = "%Y-%m-%d %H:%M:%S.%f"
+TREE_STATUS_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
+
+MESSAGE_SUFFIX = " (automatic)"
 
 
 def RunSteps(api, props):
@@ -67,6 +70,38 @@
 
     with api.context(infra_steps=True):
         status = api.tree_status.get(props.tree_status_host)
+        if props.tree_name:
+            old_status = status
+            new_status = api.tree_status.get("", tree_name=props.tree_name)
+            # If the tree status was manually set, then the two apps will be out
+            # of sync, so copy the newer status to the other app.
+            # TODO(https://fxbug.dev/332741591): Remove when we no longer need
+            # the old app for the stats dashboards.
+            if old_status.message != new_status.message:
+                if old_status.date > new_status.date:
+                    status = old_status
+                    api.tree_status.update(
+                        message=old_status.message,
+                        hostname=props.tree_status_host,
+                        admin_hostname=props.tree_status_admin_host,
+                        tree_name=props.tree_name,
+                        username=auto_closer_username,
+                        state=old_status.state.value,
+                        step_name="copy tree status to new app",
+                        last_status=new_status,
+                    )
+                else:
+                    status = new_status
+                    api.tree_status.update(
+                        message=new_status.message,
+                        hostname=props.tree_status_host,
+                        admin_hostname=props.tree_status_admin_host,
+                        username=auto_closer_username,
+                        state=new_status.state.value,
+                        step_name="copy tree status to old app",
+                        last_status=old_status,
+                    )
+
         # Under some conditions the auto-closer is guaranteed not to modify the
         # tree status regardless of the console health, in which case we can
         # exit early.
@@ -79,7 +114,9 @@
                 status=common_pb2.SUCCESS,
             )
 
-        if status.username != auto_closer_username:
+        if not (status.message.endswith(MESSAGE_SUFFIX)) or (
+            status.username and status.username != auto_closer_username
+        ):
             # If the tree is already closed by a human we shouldn't auto-reopen.
             if not status.open:
                 return RawResult(
@@ -111,9 +148,10 @@
         if not closure_reason:
             if not status.open:
                 api.tree_status.update(
-                    message="Tree is open",
+                    message="Tree is open" + MESSAGE_SUFFIX,
                     hostname=props.tree_status_host,
                     admin_hostname=props.tree_status_admin_host,
+                    tree_name=props.tree_name,
                     username=auto_closer_username,
                     state="OPEN",
                     step_name="open tree",
@@ -161,9 +199,10 @@
 
         message = f"Tree is closed: {bug_link}"
         api.tree_status.update(
-            message=message,
+            message=message + MESSAGE_SUFFIX,
             hostname=props.tree_status_host,
             admin_hostname=props.tree_status_admin_host,
+            tree_name=props.tree_name,
             username=auto_closer_username,
             state="CLOSED",
             # Trim the URL to just the ID.
@@ -490,11 +529,12 @@
         should_open_tree=False,
         rules=None,
         summary_regexp_ignore=(),
-        tree_status="open",
+        tree_status="OPEN",
         tree_status_user="human@example.com",
         tree_status_age=DEFAULT_GRACE_PERIOD + datetime.timedelta(minutes=1),
         disable_on_dates=(),
         bug_component_id=None,
+        tree_name=None,
     ):
         """Create a test case for running this recipe.
 
@@ -546,6 +586,7 @@
             tree_status_admin_host="admin.example.com",
             bug_component_id=bug_component_id,
             disable_on_dates=list(disable_on_dates),
+            tree_name=tree_name,
         )
 
         if not should_check_tree:
@@ -555,17 +596,83 @@
         res += api.time.seed(now)
 
         status_date = datetime.datetime.utcfromtimestamp(now) - tree_status_age
+        old_app_more_recent = tree_status_user != builder_name
+        old_status = tree_status
+        if tree_name:
+            old_status = "CLOSED" if tree_status == "OPEN" else "OPEN"
         res += api.step_data(
             "get current tree status",
             stdout=api.json.output(
                 {
-                    "general_state": tree_status,
+                    "general_state": (
+                        tree_status.lower()
+                        if old_app_more_recent
+                        else old_status.lower()
+                    ),
                     "username": tree_status_user,
                     "key": 12345,
-                    "date": status_date.strftime(TREE_STATUS_DATE_FORMAT),
+                    "date": status_date.strftime(OLD_TREE_STATUS_DATE_FORMAT),
+                    "message": f"Tree is {tree_status if old_app_more_recent else old_status}{MESSAGE_SUFFIX}",
                 }
             ),
         )
+        if tree_name:
+            if old_app_more_recent:
+                status_date -= datetime.timedelta(seconds=5)
+            else:
+                status_date += datetime.timedelta(seconds=5)
+            res += api.step_data(
+                "get current tree status (2)",
+                stdout=api.json.output(
+                    {
+                        "generalState": (
+                            old_status if old_app_more_recent else tree_status
+                        ),
+                        "name": f"trees/{tree_name}/status/12345",
+                        "createTime": status_date.strftime(TREE_STATUS_DATE_FORMAT),
+                        "message": f"Tree is {old_status if old_app_more_recent else tree_status}{MESSAGE_SUFFIX}",
+                    }
+                ),
+            )
+            if old_app_more_recent:
+                res += api.step_data(
+                    "check for tree status collision",
+                    stdout=api.json.output(
+                        {
+                            "generalState": old_status,
+                            "name": f"trees/{tree_name}/status/12345",
+                            "createTime": status_date.strftime(TREE_STATUS_DATE_FORMAT),
+                            "message": f"Tree is {old_status}{MESSAGE_SUFFIX}",
+                        }
+                    ),
+                )
+            else:
+                res += api.step_data(
+                    "check for tree status collision",
+                    stdout=api.json.output(
+                        {
+                            "general_state": old_status.lower(),
+                            "username": tree_status_user,
+                            "key": 12345,
+                            "date": (
+                                status_date - datetime.timedelta(seconds=5)
+                            ).strftime(OLD_TREE_STATUS_DATE_FORMAT),
+                            "message": f"Tree is {old_status}{MESSAGE_SUFFIX}",
+                        }
+                    ),
+                )
+            if should_close_tree:
+                res += api.step_data(
+                    "check for tree status collision (2)",
+                    stdout=api.json.output(
+                        {
+                            "generalState": tree_status,
+                            "name": f"trees/{tree_name}/status/12345",
+                            "createTime": status_date.strftime(TREE_STATUS_DATE_FORMAT),
+                            "message": f"Tree is {tree_status}{MESSAGE_SUFFIX}",
+                        }
+                    ),
+                )
 
         if should_check_console:
             res += api.post_process(post_process.MustRun, "check console health")
@@ -688,6 +795,7 @@
         ],
         should_close_tree=True,
         bug_component_id=12345,
+        tree_name="fuchsia-stem",
     )
 
     # Even if there are consecutive failures, ignore them if we're only checking
@@ -779,14 +887,14 @@
 
     # The build should exit early if the tree is already closed by a human.
     yield test(
-        name="tree_closed_by_human", tree_status="closed", should_check_console=False
+        name="tree_closed_by_human", tree_status="CLOSED", should_check_console=False
     )
 
     # We should not take any action if the tree is already auto-closed and the
     # console remains unhealthy.
     yield test(
         name="tree_already_closed",
-        tree_status="closed",
+        tree_status="CLOSED",
         builder_history={
             "core.x64-asan": [
                 failure(steps={"foo": "FAILURE"}),
@@ -796,13 +904,14 @@
         rules=[dict(consecutive_failures=2)],
         tree_status_user=builder_name,
         should_close_tree=False,
+        tree_name="fuchsia",
     )
 
     # The tree should be reopened if it is currently auto-closed and the console
     # is healthy.
     yield test(
         name="reopen",
-        tree_status="closed",
+        tree_status="CLOSED",
         tree_status_user=builder_name,
         builder_history={
             "bringup.arm64-asan": [success()],
@@ -816,7 +925,7 @@
         name="throttled__no_history",
         # If the tree is "throttled" then it's open, so the closer should still
         # check the console for failures.
-        tree_status="throttled",
+        tree_status="THROTTLED",
         builder_history={"core.arm64-asan": []},
         should_close_tree=False,
     )
@@ -847,7 +956,7 @@
         test(
             "nighttime_reopen_tree",
             should_check_tree=True,
-            tree_status="closed",
+            tree_status="CLOSED",
             tree_status_user=builder_name,
             builder_history={
                 "bringup.arm64-asan": [success()],