- Ручное разрешение конфликтов: при стратегии «Вручную» (Manual) синхронизация больше не решает конфликты автоматически — открывается диалог с обеими версиями задачи. Доступные действия: оставить версию Obsidian, оставить версию Singularity, создать новую задачу в Singularity (при неоднозначном сопоставлении) или пропустить до следующей синхронизации.
- Понятный интерфейс конфликт-диалога: каждая версия задачи показывается полями (статус, приоритет, проект, теги, даты, уведомления) в едином порядке, расхождения подсвечиваются бейджами «
⚠️ », сверху — сводка того, что различается. Даты отображаются в локальном часовом поясе.
- UTF-8 BOM в релизных файлах: BOM, добавленный редакторами в
manifest.json/README при сборке релиза, ломал публикацию черезrelease:publish(ошибкаJSON.parse). Файлы релиза теперь собираются без BOM,release:publishдополнительно страхуется от него.
- Ошибки 404 в консоли при синхронизации: удалённые в Singularity задачи (и проекты) генерировали ошибки в консоли разработчика и уведомления «
⚠️ API 404». Теперь ожидаемые 404 тихо возвращаютnull— логируются только реальные ошибки API. Логика синхронизации не изменилась: такие задачи корректно пропускаются.
- HTTP 400 при синхронизации: Singularity API v2 отвергает даты без таймзоны (
"2026-08-02T15:00:00") и date-only ("2026-08-02") для всех datetime-полей (start,deadline,journalDate,deleteDate). Все даты теперь отправляются с локальным timezone offset (например"2026-08-02T15:00:00+05:00") — создание и обновление задач снова работают. - Потеря дат при обновлении:
updateTask()молча отбрасывалdeadline/start/useTime— изменения сроков и напоминаний не доходили до Singularity. Исправлено. createdDateубран из создания задач: поля нет в API v2 — при отправке всегда возвращался 400.- Секция задач стиралась при синхронизации:
writeTasksToFile()заменял секцию#### 📝 Tasksпустыми строками, удаляя задачи из файлов проектов. Теперь секция мержится: поля из Singularity обновляются, Obsidian-даты (⏰🛫⏳➕✅❌) сохраняются, несовпавшие строки остаются, новые задачи добавляются в конец. - Накопление пустых строк: секция «распухала» на каждой синхронизации (до 5+ пустых строк). Теперь пустые строки схлопываются в одну-единственный разделитель.
- Исчезающий
📅: срок выполнения сохраняется из существующей строки, если Singularity вернул пустой deadline.
- Даты отправляются с локальным timezone offset (ранее — без него), в соответствии с требованиями API. Round-trip сохраняет локальное время (
⏰ 2026-08-02 15:00↔start: 2026-08-02T15:00:00+05:00).
- Task disappearance on sync: reverse sync timestamp check (
sgMod <= lastKnown) пропускал задачи сexternalId, которые только что создал forward sync. ПосколькуwriteTasksToFileзаменяет секцию целиком, пропущенные задачи удалялись из файла. Теперь задачи сexternalIdникогда не пропускаются — они всегда включаются вtasksToWrite. - Unnecessary vault.modify calls:
writeTasksToFile()вызывалvault.modify()даже при идентичном содержимом, что менялоfile.stat.mtimeи провоцировало лишние API-обновления. Теперь содержимое сравнивается перед записью.
- ⏰ Reminder support: поле
reminderDate(эмодзи⏰) — дата и время напоминания. Вычисляется изnotifies+deadline(reverse sync) или из⏰в строке задачи (forward sync). notifies→⏰forward mapping: при forward sync⏰+📅конвертируются в массивnotifies(минуты до дедлайна). Пользователь может редактировать⏰— изменения попадут в Singularity.start→⏰mapping: еслиstartв Singularity содержит время иnotifiesзадан — маппится в⏰(а не в⏳). Время только в⏰— совместимо с Obsidian Tasks Plugin.- Standard task format:
formatTaskLine()генерирует задачи в строгом порядке:#todo desc #tags ➕ ⏰ 🛫 ⏳ 📅 🔼 #notify. - StableHash (dedup):
computeStableHash()— хеш описания без метаданных (теги, даты, эмодзи). При изменении описания задача перелинковывается черезTaskStoreвместо создания дубликата. - Project transfer: перемещение задачи между проектными файлами + изменение описания корректно обновляет
projectIdв Singularity через stableHash fallback. - Normalize task order:
normalizeTaskLines()перезаписывает строки задач после forward sync в стандартном порядке. - Quiet API mode:
_req()принимаетquiet— подавляетconsole.errorиNoticeдля ожидаемых 404 (используется вsetHabitProgress()).
- Tasks wiped on sync:
writeTasksToFile()с пустым массивом задач стирал секцию#### 📝 Tasks. Теперьif (processed.length === 0) continue;предотвращает запись пустых массивов. - Completed tasks not syncing: forward sync архивировал задачи в SG, но reverse sync пропускал их (timestamp match). Добавлен трекинг
_archivedIds— reverse sync обрабатывает заархивированные задачи. - ⏰ without notifies:
startс временем генерировал⏰даже безnotifiesв API. ТеперьreminderDateустанавливается только еслиnotifies.length > 0. - Time loss on 🛫/📅:
formatTaskLine()использовал.split(" ")[0]дляstartDate/dueDate, теряя время. Исправлено — время отображается только в⏰. - Habit progress 404 Notice:
_req()показывалNoticeдля ожидаемого 404 при PATCH привычек. Добавленquietрежим. generateTaskIdregex order: emoji-даты теперь-stripаются перед эмодзи приоритета (порядокALL_EMOJI_DATE_RE→ALL_EMOJI_RE).- ⏰ only from start:
⏰теперь ТОЛЬКО из поляstart(ранее fallback изdeadline - notifies). - #notify from ⏰ only:
#notifyгенерируется только изstart, не изdeadline - start. - Local time to API:
formatDateForApi()отправляет локальное время безZ(Singularity ожидает local time, не UTC). - notifies[0] for ⏰: если
⏰задан без#notify→ отправляетсяnotifies: [0](напомнить ровно в время⏰).
notify(singular) иalarmNotify: read-only в API, больше не отправляются при создании задач.- Reverse sync timestamp skip: reverse sync пропускает SG-задачи, не изменённые с последней синхронизации (
modificatedDate <= lastModifiedSingularity).
- Cancelled task support: строки
[-]теперь корректно парсятся и записываются. Добавлен статусTaskStatus.Cancelled(checked=2). - Priority display:
🔺используется для всех задач высокой важности (вместо⏫). Оба символа⏫и🔺парсятся какPriority.High. - Null-propagation в API-клиенте: значения
nullкорректно отправляются в API для очистки полей (даты, теги). Защита от случайной зачистки SG-тегов пустым массивом. - Round-trip стабильность: Phase 2a guard проверяет статус задачи в SG перед skip-ом — предотвращает ложное срабатывание на основе хеша.
- Soft-delete защита:
syncDeletedObsidianTasks()проверяет refCount SG-задачи, не удаляет при наличии других ссылок. - lastModifiedSingularity сохранение: поле больше не теряется — сохраняется во всех upsert-операциях, гарантируя корректную работу Phase 2a.
- forward/reverse изоляция:
_failedIdsтрекает упавшие forward-задачи, reverse sync пропускает их — ошибки forward не затираются. - Done date из journalDate:
✅теперь получает дату изjournalDate(а неstart). - Date-only без дублирования эмодзи: date-only задачи не получают дублирующие
🛫/⏳. - Строгое сравнение тегов:
getTagNameиспользует точное совпадение, а неincludes(), предотвращая ложное матчинг. - projectTag в syncHash: добавлен в содержимое хеша для корректного отслеживания перемещений между проектами.
- 404-handling:
softDeleteTask/archiveTaskвозвращаютnullпри 404 без Notice/console.error.
- Per-project task files: задачи каждого проекта хранятся в отдельном файле
/project/{название}.md. Задачи без проекта — в/tasks/tasks.md. Файл = принадлежность к проекту. - Section-aware I/O: секция
#### 📝 Tasks(настраиваемый маркер) — единственное место для задач в файлах проектов. Остальное содержимое файла (frontmatter, описание, другие секции) сохраняется. - Frontmatter
singularity-id: автоматическая привязка файлов Obsidian к проектам Singularity. Позволяет отслеживать переименования проектов. - Auto-create проектов: forward sync создаёт проекты в Singularity, если Obsidian-задача ссылается на проект, которого нет в SG.
- Rename detection: при переименовании проекта в Singularity reverse sync переименовывает соответствующий файл в Obsidian.
- Create/Update Project API:
POST /project,PATCH /project/{id}. - Теги проектов в Singularity: при синхронизации forward sync создаёт тег с именем проекта в Singularity (для кросс-фильтрации).
tasksSectionMarkerв настройках: пользователь может настроить маркер секции задач (по умолчанию#### 📝 Tasks).
- Модель связывания проектов: принадлежность к проекту определяется файлом, а не тегом. Тег
#my-projectв строке задачи больше не нужен — достаточно поместить задачу в/project/My Project.md. extractProjectTag(): без эвристики первого не-todo тега. Только контекст файла определяетprojectTag. Для задач изtasks.md→null.processTask(): название проекта больше не добавляется вobsidianTags.projectTagзаполняется, но не выводится как тег.writeTasks(): группировка задач поprojectTag, роутинг в per-project файлы черезvaultWriter.writeTasksToFile().formatTaskLine(): фильтруетprojectTagизregularTags— тег проекта не выводится в строку задачи.ensureProjectFiles(): читает frontmatter дляsingularity-id, переименовывает файлы при смене названия проекта, добавляет frontmatter в существующие файлы.resolveTags(): создаёт тег проекта в Singularity (ранееprojectTagпропускался).
- Задачи в
tasks.mdс тегом#my-projectтеряют привязку к проекту. Перенесите их в/project/My Project.mdвручную. - После первого sync задачи с
externalIdавтоматически мигрируют в соответствующие проектные файлы через reverse sync. - Проектные файлы без
singularity-idполучат его автоматически при ближайшей синхронизации.
- Created date preservation: Если задача уже имеет
➕в Obsidian — дата создания сохраняется при синхронизации. Для новых задач из Singularity ставится сегодняшняя дата. - UTC→Local conversion:
utcIsoToLocalDate()конвертирует все date-only поля (createdDate, deadline, journalDate, deleteDate) из UTC ISO в локальный часовой пояс.
- Date-only scheduled/start dates: Reverse sync больше не теряет
⏳или🛫когда дата без времени. Для date-onlystartпроставляются оба поля.
- Priority emoji position: Эмодзи приоритета (
🔺⏫🔼🔽⏬) перемещён после всех emoji-дат для совместимости с плагином Obsidian Tasks.
- Reverse sync blocked: Reverse sync теперь обрабатывает задачи, уже существующие в Obsidian. Ранее
setSyncedObsidianIdsполучал все ID, из-за чего reverse sync полностью пропускал задачи, которые forward sync не создавал. - Done-статус из Singularity: Reverse sync включает архивированные задачи (
includeArchived=true). Задачи, отмеченные выполненными в Singularity, теперь отмечаются[x]в Obsidian. - Возврат из архива: Forward sync проверяет
checkedстатус SG-задачи. Если в SG задачу вернули в невыполненные, forward sync не архивирует её повторно. - Undone в Obsidian: Если SG говорит
checked=0(не выполнено), reverse sync обновляет строку в Obsidian обратно в[ ]без✅. - Duplicate строк в файле:
extractLineDescriptionи_emojiDatesReисправлены — больше не оставляют:00.000в extracted description, не плодят дубликаты. - API key leak: TaskStore.saveFn скрабит
singularityApiKeyи сохраняетsyncState+habitState. - Conflict resolver: Парсинг
modificatedDateчерезparseSgDate()— корректно обрабатывает ISO-строки, Unix-секунды и Unix-миллисекунды. - Ошибки инициализации: Guard на
view.loadState, null-safeloadData()вresetAndSync.
- Auto-create
/project/folder and project notes on first sync:ensureProjectFiles()создаёт папку/project/и.mdфайлы для каждого проекта из Singularity, если их ещё нет. Файлы содержат название, эмодзи и описание проекта. - Tasks plugin prerequisite: В README добавлен раздел с требованием установки плагина Obsidian Tasks.
_projectCacheдополнен_projectsFullдля хранения полных данных проектов (emoji, note).
- Version badge in README: Обновлён до 1.2.2 в обоих README.
- 400 error on task sync:
maxCount=5000превышал лимит API (max 1000) →GET /taskвозвращал 400. Исправлено до 1000. - Silent error swallowing: Ошибки в forward-sync и reverse-sync проглатывались пустыми
catch {}без логирования. Добавленconsole.error. - Duplicate tasks in reverse sync: Reverse sync не фильтровал задачи, только что созданные forward sync, что приводило к дублям. Добавлен
syncedObsidianIds. - Empty PATCH body:
updateTask()не обрабатывалexternalId, из-за чего вызовupdateTask(sgId, { externalId })отправлял пустой body{}→ API возвращал 400. - Missing contentType:
requestUrl()Obsidian не получалcontentType: "application/json"— Content-Type из headers мог игнорироваться.
AGENTS.md— инструкция для OpenCode-сессий (GitFlow, команды, архитектура, API-спецификация, MCP-конвенции).
updateTask()теперь принимаетexternalIdи выбрасывает ошибку при пустом теле запроса.updateExisting()проставляетexternalIdпри матчинге задач по описанию.- Улучшена диагностика:
new Noticeс телом ответа API при 400-ошибках.