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).
Wires the store task layer into the UI:
- web/task.go: handleTaskAction (create/done/reopen/edit/due/delete) routed
from handleDetailWrite at /i/{path}/task/{action}; every existing-task
mutation verifies the node belongs to the item (taskBelongsTo guard, mirrors
the CalDAV per-item calendar guard). taskBackend() type-asserts the active
Items/Writes to TaskReader/TaskWriter (nil on the legacy backend → section
omitted).
- mbrian_tasks_section.tmpl: a SECOND tasks sub-section rendered alongside the
existing CalDAV one (Q4b — both sources shown, never hide mBrian). The 'add
task' form appears only on mBrian-native projects (no caldav-list link, §3.1);
existing tasks stay toggle/editable on any project. HTMX in-place swaps like
the CalDAV section. Tasks auto-derive their slug from the title (lightweight
UX); the explicit-slug + 409 machinery stays available via the writer.
- Checklist render: 'render tasks as checklist' flag on the edit form →
metadata.projax.render; the section renders compact when set (Q1).
- taskFromTodo: CalDAV VTODO → uniform store.Task mapper (the §3.3 'two
sources, one shape' contract; the detail page keeps the richer CalDAV
rendering for ETag writeback, so the uniform-rollup consumer is a follow-up).
Unit-tested: taskFromTodo (done-state mapping, due, handle), renderHint.
NOTE to head: pre-existing web test failures (TestProjectFilter*, TestTimeline*
project-filter/timeline-route tests) fail identically on 6436b52 — they
reference the old /timeline route that now 301s to /views/timeline. Not from
this work; flagging as separate tech debt.