feat(tasks): POST /api/tasks/:id/consolidate + separate body/description fields

New endpoint manually triggers a consolidation pass for a single task.
Bypasses the auto_consolidate_tasks setting since the user is asking
explicitly. Returns the task with the freshly-written body and
consolidated_at timestamp.

Also un-aliases description and body in the create/update task routes
(was: description folded into body as legacy fallback). With separate
fields under the task-as-durable-record design, both flow through as
distinct kwargs to create_note / update_note.
This commit is contained in:
2026-05-13 12:16:43 -04:00
parent fd25d2e436
commit 9191ab5b27
+32 -4
View File
@@ -89,7 +89,11 @@ async def list_tasks_route():
async def create_task_route():
uid = get_current_user_id()
data = await request.get_json()
body = data.get("body", "") or data.get("description", "")
# Description (user-stated goal) and body (machine-maintained summary) are
# separate fields under the task-as-durable-record design. Don't fold one
# into the other.
body = data.get("body", "")
description = data.get("description")
tags = data.get("tags", [])
due_date = parse_iso_date(data.get("due_date"), "due_date")
@@ -120,6 +124,7 @@ async def create_task_route():
uid,
title=data.get("title", ""),
body=body,
description=description,
status=status,
priority=priority,
due_date=due_date,
@@ -178,11 +183,12 @@ async def update_task_route(task_id: int):
except ValueError:
return jsonify({"error": f"Invalid priority: {data['priority']}"}), 400
# Accept both "body" and "description" (prefer body)
# Body and description are distinct fields under the task-as-durable-record
# design. Don't alias one to the other.
if "body" in data:
fields["body"] = data["body"]
elif "description" in data:
fields["body"] = data["description"]
if "description" in data:
fields["description"] = data["description"]
if "due_date" in data:
if data["due_date"]:
@@ -276,3 +282,25 @@ async def delete_task_route(task_id: int):
if not deleted:
return not_found("Task")
return "", 204
@tasks_bp.route("/<int:task_id>/consolidate", methods=["POST"])
@login_required
async def consolidate_task_route(task_id: int):
"""Manually trigger a consolidation pass for a task.
Bypasses the auto_consolidate_tasks setting (the user is asking
explicitly). Returns the task's updated state including the freshly-
written body and consolidated_at timestamp.
"""
uid = get_current_user_id()
if not await can_write_note(uid, task_id):
return jsonify({"error": "Permission denied"}), 403
from fabledassistant.services.consolidation import consolidate_task
await consolidate_task(uid, task_id)
note = await get_note(uid, task_id)
if note is None:
return not_found("Task")
return jsonify(note.to_dict())