fix(restart): preserve channel metadata across /restart so reply lands in thread

cmd_restart only persisted channel + chat_id across the os.execv boundary, so
when the new process announced "Restart completed" the OutboundMessage had
no Slack thread_ts and the reply fell back to the channel root.

Serialize msg.metadata into NANOBOT_RESTART_NOTIFY_METADATA, restore it on the
RestartNotice, and forward it to OutboundMessage so the completion message
follows the same routing as the original /restart invocation.

Made-with: Cursor
This commit is contained in:
Xubin Ren
2026-04-27 12:45:00 +08:00
committed by Xubin Ren
parent 1ef41052da
commit 1fe3f0eb22
4 changed files with 65 additions and 4 deletions
+29
View File
@@ -16,6 +16,7 @@ from nanobot.utils.restart import (
def test_set_and_consume_restart_notice_env_roundtrip(monkeypatch):
monkeypatch.delenv("NANOBOT_RESTART_NOTIFY_CHANNEL", raising=False)
monkeypatch.delenv("NANOBOT_RESTART_NOTIFY_CHAT_ID", raising=False)
monkeypatch.delenv("NANOBOT_RESTART_NOTIFY_METADATA", raising=False)
monkeypatch.delenv("NANOBOT_RESTART_STARTED_AT", raising=False)
set_restart_notice_to_env(channel="feishu", chat_id="oc_123")
@@ -25,14 +26,42 @@ def test_set_and_consume_restart_notice_env_roundtrip(monkeypatch):
assert notice.channel == "feishu"
assert notice.chat_id == "oc_123"
assert notice.started_at_raw
assert notice.metadata == {}
# Consumed values should be cleared from env.
assert consume_restart_notice_from_env() is None
assert "NANOBOT_RESTART_NOTIFY_CHANNEL" not in os.environ
assert "NANOBOT_RESTART_NOTIFY_CHAT_ID" not in os.environ
assert "NANOBOT_RESTART_NOTIFY_METADATA" not in os.environ
assert "NANOBOT_RESTART_STARTED_AT" not in os.environ
def test_restart_notice_preserves_metadata_across_env(monkeypatch):
monkeypatch.delenv("NANOBOT_RESTART_NOTIFY_CHANNEL", raising=False)
monkeypatch.delenv("NANOBOT_RESTART_NOTIFY_CHAT_ID", raising=False)
monkeypatch.delenv("NANOBOT_RESTART_NOTIFY_METADATA", raising=False)
monkeypatch.delenv("NANOBOT_RESTART_STARTED_AT", raising=False)
set_restart_notice_to_env(
channel="slack",
chat_id="C123",
metadata={"slack": {"thread_ts": "1700.42", "channel_type": "channel"}},
)
notice = consume_restart_notice_from_env()
assert notice is not None
assert notice.metadata == {
"slack": {"thread_ts": "1700.42", "channel_type": "channel"}
}
assert "NANOBOT_RESTART_NOTIFY_METADATA" not in os.environ
def test_restart_notice_clears_stale_metadata(monkeypatch):
monkeypatch.setenv("NANOBOT_RESTART_NOTIFY_METADATA", '{"stale": true}')
set_restart_notice_to_env(channel="cli", chat_id="direct")
assert "NANOBOT_RESTART_NOTIFY_METADATA" not in os.environ
def test_format_restart_completed_message_with_elapsed(monkeypatch):
monkeypatch.setattr("nanobot.utils.restart.time.time", lambda: 102.0)
assert format_restart_completed_message("100.0") == "Restart completed in 2.0s."