feat(tasks): linked tasks sync title and subtasks with their note in both directions
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 2m32s

Subtasks of a task linked to a note checkbox are tied 1:1 to checkboxes of that note
(subtasks[].blockId), wherever they are moved. ReconcileDocTaskLinks now carries tick, title and
subtasks from the note to the task and adopts new checkboxes directly under the task;
SyncTaskToDoc carries tick, title and subtasks to the note and adds checkboxes for new subtasks.
Deleting is not mirrored. Deepening a task ties its subtasks right away; saves without links keep
the server's links. The sync response reports taskLinks/subtaskLinks; the editor shows an
"Unteraufgabe" badge. v0.36.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-11-01 12:29:10 +01:00
1 parent f91926c208
commit 5a8922011d
20 files changed
+511 -53

No files matched your search

+4
View File
@@ -39,6 +39,9 @@ type syncResponse struct {
Sessions int `json:"sessions"`
Presence []collab.Presence `json:"presence"`
Leases syncLeases `json:"leases"`
// Checkboxes linked to a task or to a subtask (block → task), for the editor's badges
TaskLinks map[string]string `json:"taskLinks"`
SubtaskLinks map[string]string `json:"subtaskLinks"`
}
// docAccess resolves the caller's role; it writes 404 and returns "" without access.
@@ -171,6 +174,7 @@ func (s *Server) handleDocSync(w http.ResponseWriter, r *http.Request) {
resp.HeadSeq = head
resp.HasMore = len(entries) == syncMaxLog && entries[len(entries)-1].Seq < head
resp.Shared = s.store.IsDocShared(docID)
resp.TaskLinks, resp.SubtaskLinks = s.store.DocTaskLinks(docID)
resp.Sessions, resp.Presence = s.leases.Sessions(docID)
resp.Leases.Others = s.leases.Others(docID, req.ClientID)
+6
View File
@@ -205,6 +205,12 @@ func (s *Server) handleCreateEnhancedTask(w http.ResponseWriter, r *http.Request
writeError(w, http.StatusBadRequest, err.Error())
return
}
// Checkboxes already right under it become its subtasks (task_doc_sync.go)
if len(s.store.ReconcileDocTaskLinks(docID)) > 0 {
if fresh, err := s.store.GetThoughtByIDUnchecked(thought.ID); err == nil && fresh != nil {
thought = fresh
}
}
_, head, _ := s.store.GetDocOpsSince(docID, 0, 0)
s.notifyDocChanged(docID, head)
+1 -1
View File
@@ -320,7 +320,7 @@ func (s *Server) updateTask(userID, thoughtID string, req taskPatch) (*domain.Th
}
updated.Assignees = assignees
}
if docID := s.store.SyncDocBlockAndTask(userID, thoughtID, updated.IsCompleted); docID != "" {
if docID := s.store.SyncTaskToDoc(userID, thoughtID); docID != "" {
_, head, _ := s.store.GetDocOpsSince(docID, 0, 0)
s.notifyDocChanged(docID, head)
}