rebuildCache (cacheOperation.ts):
- syncData not refreshed after initializeSync, breaking legacy ID conversion
- checked field 0/1 vs boolean coercion causing false conflict detection
- ID conversion branch missing status/syncEnabled fields
- 6 direct settings mutations replaced with local nextMapping + single safeSettings.update
- serial GetTaskById replaced with in-memory Map lookup
- removed 4 debug console.log statements left in production code
- resolveConflicts: added TFile type guard for vault.read
- removed dead DatabaseCheckIssue/Result interfaces (~100 lines)
databaseChecker:
- checked field 0/1 vs boolean causing false status_mismatch reports
- priority check extracting from labels instead of !!<n> text format (disabled)
- Object.assign overwrote previously accumulated summary counts
- error catch used wrong issue type (task_not_in_vault → unknown_issue)
- deduplicated identical Vault❌+Mapping✅ branches (~30 lines)
- startup check made non-blocking to avoid slow plugin init
fileOperation (shared by both modules):
- getTodoistTasksFromSyncData: task.projectId → task.project_id (snake_case)
- scanVaultTasks: exclude plugin storage directory and hidden dirs
settings optimization (from prior analysis):
- strip 7 ghost fields from data.json on load (3.5MB → ~160KB)
- remove dead statistics and deviceIdGenerated from interface
- safeSettings backup throttled to once per 5 minutes
- sanitize corrupted taskFileMapping entries on load
- fullTextNewTaskCheck: passed line text instead of line index to setTaskFileMapping
- convertTextToTodoistTaskObject: parentTaskObject.projectId → project_id (Sync API snake_case)
- taskTagCompare: clone arrays before sort to prevent mutating syncData cache
- taskStatusCompare: use !! coercion for checked (0/1 vs boolean)
- compareTaskDueDate: rewrite dead code, compare date strings directly (no timezone conversion)
- localDateStringToUTCDateString: remove T08:00 hardcode that broke non-UTC+8 timezones
- getAllTagsFromLineText: return [] instead of null when no tags
- lineModifiedTaskCheck: remove dead projectModified/parentIdModified comparisons
- fullTextModifiedTaskCheck: remove unused hasModifiedTask variable
- syncTodoistToObsidian now uses incremental sync + updated_at field comparison
instead of unreliable Activities API with client string filtering
- Naturally deduplicates: same updated_at = no action (idempotent)
- Field-level comparison: checked, content, due date synced individually
- Note sync via note_count tracking in taskFileMapping
- Extended taskFileMapping with updated_at and note_count fields
- Fixed checkboxEventhandle: await closeTask/repoenTask + try/finally for syncLock
- Added withSyncLock() helper to prevent lock leaks across all sync operations
- scheduledSynchronization: added re-entry guard, changed return→continue in file loop
- Removed obsolete Activity event pass-through methods from syncModule
- TODOIST_LINK regex now matches both https://todoist.com and todoist:// URIs
- Added REMOVE_TODOIST_LINK_APP_URI regex to strip todoist://task?id=xxx from task content
- getTaskContentFromLineText() now removes app URI links before sending content to API
- generateTempId() now uses UUID format instead of 'temp_' prefix (Todoist API requires UUID)
- AddTask() no longer sends empty project_id when defaultProjectId is unset (API defaults to Inbox)
- executeCommands() now validates sync_status and logs per-command API errors
- addTask() error messages now include actual API error details instead of generic 'no response'
- cleanOldBackups() checks file existence before removal to avoid ENOENT on concurrent cleanup
- generateTempId() now uses UUID format instead of 'temp_' prefix (Todoist API requires UUID)
- AddTask() no longer sends empty project_id when defaultProjectId is unset (API defaults to Inbox)
- executeCommands() now validates sync_status and logs per-command API errors
- addTask() error messages now include actual API error details instead of generic 'no response'
- Fix executeCommands() using response.json()/text() as functions instead of properties (Obsidian requestUrl returns properties, not methods) — all write operations were crashing
- Fix reopenTask() using non-existent 'item_reopen' Sync API command, correct command is 'item_uncomplete' per official docs
- Fix getNonObsidianAllActivityEvents() returning undefined on error, causing caller to crash on .filter()
- Fix 6 getter methods (GetAllProjects, GetTaskById, etc.) discarding getAllResources() return value instead of assigning to syncData
- Fix backupTodoistAllResources() using incremental sync instead of full sync for backups
- Fix startup not performing incremental sync after loading from cache, leaving data stale
editor-change handler called saveSettings() regardless of whether a new
task was found. lineContentNewTaskCheck() already saves internally when
a task is actually created, making the outer call redundant and causing
unnecessary disk writes on every keystroke.
- Remove backup logic from saveSettings() (now owned by SafeSettings)
- Convert all direct this.settings.xxx = yyy mutations to safeSettings.update()
- Add SafeSettings.reset() for wholesale settings replacement
- Files changed: main.ts, settings.ts, todoistSyncAPI.ts, storagePathManager.ts, cacheOperation.ts, safeSettings.ts
- Zero direct settings mutations remain in source code
- Add check for settingsBackup existence before backup
- Warn if settingsBackup not initialized instead of silently skipping
- Simplify error handling with proper restore attempt
- Remove updateSync (duplicate)
- Make setTaskFileMapping and deleteTaskFileMapping async
- All settings updates now use await safeSettings.update()
- Simplifies code and ensures proper async handling
- Replace direct settings assignments with safeSettings.update/updateSync
- setTaskFileMapping and deleteTaskFileMapping use updateSync (no immediate save)
- updateRenamedFilePath and rebuildCache use update with shouldSave=true
- Provides automatic backup and rollback on failure
- Modify deletedTaskCheck() to return number of deleted tasks
- Only call saveSettings() when deletedCount > 0
- This prevents unnecessary SettingsBackup triggers on every Delete/Backspace keypress
- Change LogAction to string type for flexibility
- Rename todayLogs to memoryLogs
- Use single log file (todoist-sync-logs.json)
- Add size-based cleanup (maxLogFileSize, logRetentionPercent)
- Remove batch buffer, use memoryLogs directly
- Update settings with new log config options
- Remove legacy logs array and logRetentionDays
- Fix filename regex to match actual format (deviceId_date.json)
- Load today's logs from file on startup
- Implement maxLogs limit (500 entries)
- Flush log buffer after scheduled sync completes
- Flush log buffer on plugin unload
- Remove todayLogs from settings (no longer persisted)
- Add batch write logic: write to JSON file every 20 logs
- Remove FILE_MODIFIED log to reduce unnecessary logging
- Logs are now stored only in JSON files, not in data.json
- Since fileMetadata now only stores defaultProjectId (no longer todoistTasks array)
- Changed sync loop to derive file list from taskFileMapping
- Uses Set to get unique file paths from taskFileMapping
- Fixes sync functionality that was broken after fileMetadata refactor
- Remove todoistTasks and todoistCount from fileMetadata (can be derived from taskFileMapping)
- Simplify fileMetadata type to Record<string, { defaultProjectId?: string }>
- Add helper methods getTasksInFile() and getTaskCountInFile() to derive task info
- Remove deprecated deleteTaskIdFromMetadata() method
- Update comments to reflect new data structure
- Eliminates data redundancy and avoids sync issues
- Add getReportsPath() method to StoragePathManager
- Add reports directory to ensureAllDirs()
- Save database check reports to ultimate-todoist-sync/reports/ instead of vault root
- Change default storage dir from .ultimate-todoist-sync to ultimate-todoist-sync (visible in file manager)
- Add storageDirectory and lastStorageDirectory to settings
- Implement migrateToNewDirectory() for automatic data migration when directory changes
- Smart merge: keep newer files during migration (compare mtime)
- Fix listFiles() to use adapter.list() instead of vault.getFiles()
- Add Storage Directory setting in Backup & Recovery section
- Update BackupOperation and TodoistToObsidian to use new dynamic paths
- vault.getFiles() doesn't return files in hidden directories (starting with '.')
- Changed getLatestBackup(), getBackupList(), and cleanOldBackups() to use adapter.list() and adapter.stat()
- This fixes the 'No backups found' issue in settings UI
- Add file existence verification after backup writes
- Implement atomic write with temp file pattern for all backups
- Add content validation in restoreFromBackup()
- Use StoragePathManager constants for all backup paths
- Rollback initialized state on initialization failure
- Add backup history view button in settings UI
- Add temp file check on load (recoverFromTempFile)
- Add JSON validation before backup/restore operations
- Use StoragePathManager constants for settings paths
- Add user notifications for backup failures
- Initialize settingsBackup earlier in loadSettings
- Add Notice messages for better user feedback
- Refactor settingsBackup to use centralized path constants
- Add saveLock to prevent concurrent writes
- Create SettingsBackup class for automatic backup before save
- Implement atomic write with backup/restore on failure
- Add validation on load with auto-recovery from backup
- Add settings backup UI in settings tab (backup/restore/reset)
- Integrate with storagePathManager for unified path management