Supersedes the Phase 7b two-section split (m: 'tasks section should collect
from mBrian AS WELL AS CalDAV and display together'). The detail page now
renders a single merged task list per project:
- buildUnifiedTasks merges mBrian-native tasks (TasksForItem) + CalDAV VTODOs
(detailTodos → taskFromTodo) into one open/done split via the uniform
store.Task shape. Each row carries a subtle source label (calendar name for
CalDAV, 'projax' for mBrian). Sorted: dated-before-undated, earlier due
first, then created, then title.
- Row actions dispatch by Source: CalDAV rows POST /caldav/todo/{action}
(calendar_url+uid, ETag writeback); mBrian rows POST /task/{action}
(node_id). One template, branch on .Source.
- ONE add-form, backend by §3.1 selector (unifiedTasks.AddTarget): CalDAV on a
bound project (hidden calendar_url for a single list, a <select> when
several), mBrian-native on an unbound project. New-task default per m's spec.
- Both handlers (handleCalDAVTodoAction + handleTaskAction) now re-render the
SAME merged fragment via renderUnifiedTasks, so a write from either backend
refreshes the unified list in place.
- Retired the two-section split: deleted mbrian_tasks_section.tmpl + its
registration/render-case, rewrote tasks_section.tmpl as the unified list,
removed renderTasksSection. CalDAV link/create-list affordances preserved.
Unit-tested: sortTaskRows (merge order), AddTarget (backend selection across
bound/unbound × single/multi-calendar). Updated TestDetailLinkExistingCalendar
to the unified UI (no per-calendar block; bound project → add-form targets the
linked calendar, create-new hidden). caldav/gitea/mcp/internal green; the 8
remaining web failures are the pre-existing TestProjectFilter*/TestTimeline*
route-drift (fail on 6436b52).