Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 23 additions & 16 deletions nerve/agent/tools/handlers/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,9 +294,6 @@ async def task_update_handler(ctx: ToolContext, args: dict) -> ToolResult:
if not task:
return ToolResult.text(f"Task not found: {task_id}")

if status:
await ctx.db.update_task_status(task_id, status)

new_tags_str = ""
if raw_tags:
current_tags = set(parse_tags_string(task.get("tags", "") or ""))
Expand All @@ -311,8 +308,6 @@ async def task_update_handler(ctx: ToolContext, args: dict) -> ToolResult:
else:
new_tags_str = tags_to_string(parse_tags_string(raw_tags))

await ctx.db.update_task_tags(task_id, new_tags_str)

if ctx.workspace and (note or deadline or raw_tags or new_title):
file_path = ctx.workspace / task["file_path"]
if file_path.exists():
Expand All @@ -321,17 +316,6 @@ async def task_update_handler(ctx: ToolContext, args: dict) -> ToolResult:
)
if new_title:
content = re.sub(r"^# .+", f"# {new_title}", content, count=1)
await ctx.db.upsert_task(
task_id=task_id,
file_path=task["file_path"],
title=new_title,
status=status or task["status"],
source=task.get("source"),
source_url=task.get("source_url"),
deadline=deadline or task.get("deadline"),
tags=new_tags_str if raw_tags else (task.get("tags") or ""),
content=content,
)
if note:
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
content += f"\n- {today}: {note}"
Expand All @@ -358,6 +342,29 @@ async def task_update_handler(ctx: ToolContext, args: dict) -> ToolResult:
file_path.write_text, content, encoding="utf-8",
)

final_title = new_title or task["title"]
final_status = status or task["status"]
final_deadline = deadline or task.get("deadline")
final_tags = new_tags_str if raw_tags else (task.get("tags") or "")
await ctx.db.upsert_task(
task_id=task_id,
file_path=task["file_path"],
title=final_title,
status=final_status,
source=task.get("source"),
source_url=task.get("source_url"),
deadline=final_deadline,
tags=final_tags,
content=content,
)
return ToolResult.text(f"Task {task_id} updated.")

# Fall back to metadata updates when no task file was changed.
if status:
await ctx.db.update_task_status(task_id, status)
if raw_tags:
await ctx.db.update_task_tags(task_id, new_tags_str)

return ToolResult.text(f"Task {task_id} updated.")


Expand Down
48 changes: 48 additions & 0 deletions tests/test_task_statuses.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,54 @@ async def test_update_accepts_valid_status(self, db: Database, tmp_path):
await task_update_handler(ctx, {"task_id": task_id, "status": "in_progress"})
assert (await db.get_task(task_id))["status"] == "in_progress"

async def test_update_deadline_syncs_database(self, db: Database, tmp_path):
ctx = self._ctx(db, tmp_path)
await task_create_handler(ctx, {"title": "Deadline task", "content": "x"})
task_id = (await db.list_tasks(status="all"))[0]["id"]

await task_update_handler(ctx, {"task_id": task_id, "deadline": "2026-08-01"})

task = await db.get_task(task_id)
assert task["deadline"] == "2026-08-01"
assert "**Deadline:** 2026-08-01" in (tmp_path / task["file_path"]).read_text(
encoding="utf-8",
)

async def test_update_note_refreshes_fts(self, db: Database, tmp_path):
ctx = self._ctx(db, tmp_path)
await task_create_handler(ctx, {"title": "Search task", "content": "x"})
task_id = (await db.list_tasks(status="all"))[0]["id"]

await task_update_handler(ctx, {"task_id": task_id, "note": "Investigated quasarflux"})

results = await db.search_tasks("quasarflux")
assert [task["id"] for task in results] == [task_id]

async def test_update_combined_fields_indexes_final_state(
self, db: Database, tmp_path,
):
ctx = self._ctx(db, tmp_path)
await task_create_handler(ctx, {"title": "Original title", "content": "x"})
task_id = (await db.list_tasks(status="all"))[0]["id"]

await task_update_handler(
ctx,
{
"task_id": task_id,
"title": "Renamed task",
"status": "in_progress",
"deadline": "2026-08-01",
"tags": "ops,urgent",
"note": "Found nebularift",
},
)

task = await db.get_task(task_id)
assert (task["title"], task["status"], task["deadline"], task["tags"]) == (
"Renamed task", "in_progress", "2026-08-01", "ops,urgent",
)
assert [task["id"] for task in await db.search_tasks("nebularift")] == [task_id]

async def test_status_create_handler(self, db: Database, tmp_path):
ctx = self._ctx(db, tmp_path)
result = await task_status_create_handler(
Expand Down