diff --git a/nerve/agent/tools/handlers/tasks.py b/nerve/agent/tools/handlers/tasks.py index cc7eea23..541b8825 100644 --- a/nerve/agent/tools/handlers/tasks.py +++ b/nerve/agent/tools/handlers/tasks.py @@ -474,6 +474,10 @@ def _move_to_done() -> None: file_path=rel_path, title=task["title"], status="done", + source=task.get("source"), + source_url=task.get("source_url"), + deadline=task.get("deadline"), + tags=task.get("tags") or "", content=content, ) diff --git a/nerve/tasks/manager.py b/nerve/tasks/manager.py index 7c00e721..25d64a56 100644 --- a/nerve/tasks/manager.py +++ b/nerve/tasks/manager.py @@ -101,6 +101,10 @@ def _move_to_done() -> None: file_path=rel_path, title=row["title"], status="done", + source=row.get("source"), + source_url=row.get("source_url"), + deadline=row.get("deadline"), + tags=row.get("tags") or "", content=content, ) diff --git a/tests/test_task_completion.py b/tests/test_task_completion.py new file mode 100644 index 00000000..6cb15150 --- /dev/null +++ b/tests/test_task_completion.py @@ -0,0 +1,69 @@ +"""Regression tests for completing tasks.""" + +from __future__ import annotations + +import pytest + +from nerve.agent.tools.handlers.tasks import task_done_handler +from nerve.agent.tools.registry import ToolContext +from nerve.db import Database +from nerve.tasks.manager import TaskManager + + +TASK_METADATA = { + "source": "github", + "source_url": "https://github.com/example/project/issues/123", + "deadline": "2026-08-02", + "tags": "bug,github,urgent", +} + + +async def _create_task(db: Database, tmp_path, task_id: str) -> None: + active_dir = tmp_path / "memory" / "tasks" / "active" + active_dir.mkdir(parents=True, exist_ok=True) + task_file = active_dir / f"{task_id}.md" + content = "# Preserve metadata\n\nInvestigate source synchronization.\n" + task_file.write_text(content, encoding="utf-8") + await db.upsert_task( + task_id=task_id, + file_path=str(task_file.relative_to(tmp_path)), + title="Preserve metadata", + status="pending", + content=content, + **TASK_METADATA, + ) + + +async def _assert_metadata_preserved(db: Database, task_id: str) -> None: + task = await db.get_task(task_id) + assert task is not None + assert task["status"] == "done" + assert {field: task[field] for field in TASK_METADATA} == TASK_METADATA + assert task["file_path"].startswith("memory/tasks/done/") + + matches = await db.search_tasks( + query="source synchronization", status="all", tag="urgent", + ) + assert [match["id"] for match in matches] == [task_id] + + +@pytest.mark.asyncio +async def test_task_done_handler_preserves_metadata(db: Database, tmp_path): + task_id = "2026-07-30-handler-metadata" + await _create_task(db, tmp_path, task_id) + + ctx = ToolContext(session_id="test", db=db, workspace=tmp_path) + await task_done_handler(ctx, {"task_id": task_id}) + + await _assert_metadata_preserved(db, task_id) + + +@pytest.mark.asyncio +async def test_task_manager_mark_done_preserves_metadata(db: Database, tmp_path): + task_id = "2026-07-30-manager-metadata" + await _create_task(db, tmp_path, task_id) + + manager = TaskManager(tmp_path, db) + assert await manager.mark_done(task_id) + + await _assert_metadata_preserved(db, task_id)