From 15c2bd25b31c10aa99dc93c7449f530fdbd8ba9d Mon Sep 17 00:00:00 2001 From: chengyongru <2755839590@qq.com> Date: Sun, 31 May 2026 15:02:34 +0800 Subject: [PATCH] refactor(heartbeat): remove Completed section and tighten section gating - Remove ## Completed section from HEARTBEAT.md template; completed tasks should be deleted, not accumulated - Change in_active_section from tri-state (None/True/False) to bool (True/False) so stray text before any ## heading no longer triggers heartbeat - Add test cases for stray pre-heading text and ## Notes section - Update docs/chat-commands.md to reference ## Active Tasks --- docs/chat-commands.md | 6 +++--- nanobot/cli/commands.py | 2 +- nanobot/templates/HEARTBEAT.md | 6 +----- tests/cli/test_commands.py | 4 +++- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/docs/chat-commands.md b/docs/chat-commands.md index 123386c8f..de4f6e3a0 100644 --- a/docs/chat-commands.md +++ b/docs/chat-commands.md @@ -56,17 +56,17 @@ Preset names come from the top-level `modelPresets` config. Switching is runtime ## Periodic Tasks -The gateway wakes up every 30 minutes and checks `HEARTBEAT.md` in your workspace (`~/.nanobot/workspace/HEARTBEAT.md`). If the file has tasks, the agent executes them and delivers results to your most recently active chat channel. +The gateway wakes up every 30 minutes and checks `HEARTBEAT.md` in your workspace (`~/.nanobot/workspace/HEARTBEAT.md`). If the file has tasks under `## Active Tasks`, the agent executes them and delivers results to your most recently active chat channel. If there are no active tasks, the heartbeat is skipped silently. **Setup:** edit `~/.nanobot/workspace/HEARTBEAT.md` (created automatically by `nanobot onboard`): ```markdown -## Periodic Tasks +## Active Tasks - [ ] Check weather forecast and send a summary - [ ] Scan inbox for urgent emails ``` -The agent can also manage this file itself — ask it to "add a periodic task" and it will update `HEARTBEAT.md` for you. +The agent can also manage this file itself — ask it to "add a periodic task" and it will update `HEARTBEAT.md` for you. Completed tasks should be deleted from the file, not moved to another section. > **Note:** The gateway must be running (`nanobot gateway`) and you must have chatted with the bot at least once so it knows which channel to deliver to. diff --git a/nanobot/cli/commands.py b/nanobot/cli/commands.py index 4d30d605e..bf1dc55a2 100644 --- a/nanobot/cli/commands.py +++ b/nanobot/cli/commands.py @@ -107,7 +107,7 @@ _HEARTBEAT_PREAMBLE = ( def _heartbeat_has_active_tasks(content: str) -> bool: """True if HEARTBEAT.md has task lines, ignoring headers, blanks and comments.""" in_comment = False - in_active_section: bool | None = None + in_active_section: bool = False for line in content.splitlines(): stripped = line.strip() if in_comment: diff --git a/nanobot/templates/HEARTBEAT.md b/nanobot/templates/HEARTBEAT.md index 9461f787c..e29f64d41 100644 --- a/nanobot/templates/HEARTBEAT.md +++ b/nanobot/templates/HEARTBEAT.md @@ -5,14 +5,10 @@ This file is checked periodically by your nanobot agent. Register it as a cron job (e.g. `cron add --name heartbeat --schedule "every 30m" --message "Check HEARTBEAT.md"`) to get the same behavior as the legacy heartbeat service. If this file has no tasks (only headers and comments), the agent will skip it. +Completed tasks should be deleted, not kept — heartbeat only reads "Active Tasks". --> ## Active Tasks - -## Completed - - - diff --git a/tests/cli/test_commands.py b/tests/cli/test_commands.py index eea27320a..af16d7a67 100644 --- a/tests/cli/test_commands.py +++ b/tests/cli/test_commands.py @@ -960,8 +960,10 @@ def test_heartbeat_retains_recent_messages_by_default(): ("\n", False), # block comment, not tasks ("\n", False), ("## Active Tasks\n\n- water the plants\n", True), - ("## Completed\n\n- water the plants\n", False), ("## Active Tasks\n\n### Garden\n\n- water the plants\n", True), + ("## Notes\n\nsome random note\n", False), + ("stray text before any heading\n## Active Tasks\n\n- task\n", True), + ("stray text before any heading\n", False), ], ) def test_heartbeat_has_active_tasks(content, expected):