upsert_note_by_title_project
- Category: tasks
- Access: write
- Audience: ordinary (an ordinary user normally calls this directly)
Purpose
Creates a note or updates the existing note with the same normalized title within the same project. The idempotent write surface for durable research and decision notes: repeated runs converge on one note instead of creating near-duplicates.
Arguments
Required: title.
Optional: description, notes, priority, project, section, update_if_found.
-
title: Note title (required). -
description: Primary long-form note body. Note: Primary long-form content. notes is only for auxiliary or machine-readable metadata. -
notes: Optional auxiliary/internal metadata. -
priority: Priority for new notes; updates only when explicitly set. -
project: Project tag for matching and grouping. Note: Empty or absent means untagged, which needs an explicit untagged grant. -
section: Section for new notes; updates only when explicitly set. Note: Defaults to next and priority to medium on creation; on update they change only when explicitly set. -
update_if_found: When false, return the existing row without mutation. Note: When false, a matched note is returned untouched with action existing.
Result
JSON object with task_id, title, type (always note), action (created, updated, or existing), and matched_on (always normalized_title_project). Creation also returns status not_started; updates return the changed fields list.
Boundaries
Matching and creation stay inside the selected profile boundary and the caller's project grants. Out-of-scope projects deny with a generic scope message; untagged writes fail closed without the untagged grant.
Lifecycle / side effects
Creates the note or applies the supplied fields through the canonical mutation path, recording an audit/history event. Matching is by normalized title plus project. No undo tool exists; later movement uses update_task, which preserves history per the no-delete invariant.
Errors
Blank title is rejected. Unknown arguments are rejected (strict validator). Out-of-scope projects deny generically. If the operation fails, nothing is partially created (atomic).
Example
{
"description": "Chosen because WAL tolerates concurrent writers.",
"project": "shared-ops",
"title": "Bus Timeout Decision"
}
Result shape:
{
"action": "created",
"matched_on": "normalized_title_project",
"status": "not_started",
"task_id": "<uuid>",
"title": "Bus Timeout Decision",
"type": "note"
}
task_id is assigned by the server; the placeholder stands for the returned UUID.
See also
create_task_or_note, update_task, query_tasks, find_by_title.