diff --git a/CHANGELOG.md b/CHANGELOG.md
index a58e639..1535cef 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## Next version
+
+### ✨ Improved
+
+* [#77](https://github.com/sdss/clu/issues/77) Child commands will never emit ``:`` or ``f`` messages that may be confused as the parent being done.
+
+
 ## 1.1.2 - May 31, 2021
 
 ### 🔧 Fixed
diff --git a/python/clu/actor.py b/python/clu/actor.py
index 8c80281..8a29502 100644
--- a/python/clu/actor.py
+++ b/python/clu/actor.py
@@ -424,7 +424,7 @@ class JSONActor(ClickParser, TCPBaseActor):
 async def multiline(command: Command, on: bool):
     """Set multiline mode for the transport."""
 
-    transport: CustomTransportType = getattr(command, "transport", None)
+    transport = getattr(command, "transport", None)
     if not transport:
         return command.fail("The command has no transport.")
 
diff --git a/python/clu/command.py b/python/clu/command.py
index a10688b..686a1c1 100644
--- a/python/clu/command.py
+++ b/python/clu/command.py
@@ -265,6 +265,14 @@ class BaseCommand(
 
         command = self if not self.parent else self.parent
 
+        # If the parent has a command, do not output : or f since it would
+        # confuse the stream and potentially Tron.
+        if self.parent:
+            if message_code == ":":
+                message_code = "i"
+            elif message_code == "f":
+                message_code = "e"
+
         self.actor.write(
             message_code,
             message=message,

diff --git a/tests/test_command.py b/tests/test_command.py
index a920549..6d37678 100644
--- a/tests/test_command.py
+++ b/tests/test_command.py
@@ -86,6 +86,30 @@ def test_child_command_write(command):
     )
 
 
+def test_child_command_finished(command):
+
+    child = Command(command_string="new-command", parent=command)
+
+    child.finish(text="Finished")
+    command.actor.write.assert_called_with(
+        "i", message={}, text="Finished", command=command, broadcast=False, **{}
+    )
+
+    assert child.status.did_succeed
+
+
+def test_child_command_failed(command):
+
+    child = Command(command_string="new-command", parent=command)
+
+    child.fail(error="Failed")
+    command.actor.write.assert_called_with(
+        "e", message={}, error="Failed", command=command, broadcast=False, **{}
+    )
+
+    assert child.status.did_fail
+
+
 def test_write_str(command):
 
     command.write("i", "hello")

Avoid child commands to finish parent
Child commands should not finish or fail the parent command, I think. Instead `finish` can probably be output as `info` and `fail` as `error`.

Also, not set the command running again if it already is.
tests/test_command.py::test_child_command_finished tests/test_command.py::test_child_command_failed
tests/test_command.py::test_command tests/test_command.py::test_set_status tests/test_command.py::test_set_done_command tests/test_command.py::test_set_status_fails tests/test_command.py::test_set_status_int tests/test_command.py::test_set_status_str tests/test_command.py::test_set_status_str_fails tests/test_command.py::test_child_command tests/test_command.py::test_child_command_write tests/test_command.py::test_write_str tests/test_command.py::test_write_dict tests/test_command.py::test_write_bad_message tests/test_command.py::test_write_no_actor tests/test_command.py::test_wait_for_status tests/test_command.py::test_status_callback