From 6873c8d5a51af0bfe763e22fbe8dbdbf47cd372d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Normen=20Mu=CC=88ller?= Date: Fri, 2 Jan 2026 13:43:52 +0100 Subject: [PATCH 1/4] fix: preserve tags in property-based identification (#1391) --- PR.md | 19 +++++++++++++++++++ src/services/TaskService.ts | 36 +++++++++++++----------------------- 2 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 PR.md diff --git a/PR.md b/PR.md new file mode 100644 index 00000000..268e1c50 --- /dev/null +++ b/PR.md @@ -0,0 +1,19 @@ +# fix-task-prop-based-ident + +## Property-based task identification preserves tags + +This change prevents property-based identification from mutating tags automatically. Tags are only written when the user explicitly sets them (including default tags), while the identifying property is still applied. + +Examples (illustrative): + +- Creating a task with property identification and tags like `client, urgent` keeps those tags; no extra task tag is injected. +- Updating priority/status without touching tags leaves existing tags unchanged. + +## Changelog + +### Task Identification + +- Only add the identifying task tag to frontmatter when tag-based identification is enabled. +- In property-based mode, preserve user/default tags without filtering them out. +- Only write `frontmatter.tags` during updates when tags are explicitly provided; otherwise leave tags untouched. +- Fixes callumalpass/tasknotes#1391. diff --git a/src/services/TaskService.ts b/src/services/TaskService.ts index 80b6a347..0560df66 100644 --- a/src/services/TaskService.ts +++ b/src/services/TaskService.ts @@ -343,10 +343,15 @@ export class TaskService { icsEventId: taskData.icsEventId || undefined, }; + const shouldAddTaskTag = this.plugin.settings.taskIdentificationMethod === "tag"; + const taskTagForFrontmatter = shouldAddTaskTag + ? this.plugin.settings.taskTag + : undefined; + // Use field mapper to convert to frontmatter with proper field mapping const frontmatter = this.plugin.fieldMapper.mapToFrontmatter( completeTaskData, - this.plugin.settings.taskTag, + taskTagForFrontmatter, this.plugin.settings.storeTitleInFilename ); @@ -361,12 +366,8 @@ export class TaskService { lower === "true" || lower === "false" ? lower === "true" : propValue; frontmatter[propName] = coercedValue as any; } - // Remove task tag from tags array if using property identification - const filteredTags = tagsArray.filter( - (tag: string) => tag !== this.plugin.settings.taskTag - ); - if (filteredTags.length > 0) { - frontmatter.tags = filteredTags; + if (tagsArray.length > 0) { + frontmatter.tags = tagsArray; } } else { // Tags are handled separately (not via field mapper) @@ -1374,23 +1375,12 @@ export class TaskService { } if (updates.hasOwnProperty("tags")) { - let tagsToSet = updates.tags; - // Remove task tag if using property identification - if (this.plugin.settings.taskIdentificationMethod === "property" && tagsToSet) { - tagsToSet = tagsToSet.filter( - (tag: string) => tag !== this.plugin.settings.taskTag - ); - } - frontmatter.tags = tagsToSet; - } else if (originalTask.tags) { - let tagsToSet = originalTask.tags; - // Remove task tag if using property identification - if (this.plugin.settings.taskIdentificationMethod === "property") { - tagsToSet = tagsToSet.filter( - (tag: string) => tag !== this.plugin.settings.taskTag - ); + const tagsToSet = Array.isArray(updates.tags) ? [...updates.tags] : []; + if (tagsToSet.length > 0) { + frontmatter.tags = tagsToSet; + } else { + delete frontmatter.tags; } - frontmatter.tags = tagsToSet; } }); From 8bb4293330c2930ea0996c773486034647468dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Normen=20Mu=CC=88ller?= Date: Sat, 3 Jan 2026 21:53:55 +0100 Subject: [PATCH 2/4] fix: preserve tags in edit modal for property mode --- src/modals/TaskEditModal.ts | 12 ++++++----- .../issue-843-property-mode-tag-bug.test.ts | 20 +++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/modals/TaskEditModal.ts b/src/modals/TaskEditModal.ts index 64f7b90e..aaa41f3c 100644 --- a/src/modals/TaskEditModal.ts +++ b/src/modals/TaskEditModal.ts @@ -97,11 +97,13 @@ export class TaskEditModal extends TaskModal { this.selectedProjectItems = []; } - this.tags = this.task.tags - ? sanitizeTags( - this.task.tags.filter((tag) => tag !== this.plugin.settings.taskTag).join(", ") - ) - : ""; + const shouldFilterTaskTag = + this.plugin.settings.taskIdentificationMethod === "tag"; + const rawTags = this.task.tags || []; + const visibleTags = shouldFilterTaskTag + ? rawTags.filter((tag) => tag !== this.plugin.settings.taskTag) + : rawTags; + this.tags = rawTags.length > 0 ? sanitizeTags(visibleTags.join(", ")) : ""; this.timeEstimate = this.task.timeEstimate || 0; // Handle recurrence diff --git a/tests/unit/issues/issue-843-property-mode-tag-bug.test.ts b/tests/unit/issues/issue-843-property-mode-tag-bug.test.ts index ebb234f0..4f0134f4 100644 --- a/tests/unit/issues/issue-843-property-mode-tag-bug.test.ts +++ b/tests/unit/issues/issue-843-property-mode-tag-bug.test.ts @@ -215,6 +215,16 @@ describe('Issue #843: Property Mode Tag Bug', () => { expect(changes.tags).not.toContain('task'); } }); + + test('should preserve task tag on load in property mode', async () => { + mockTask.tags = ['task', 'existing']; + const modal = new TaskEditModal(mockApp, mockPlugin, { task: mockTask }); + (modal as any).initializeSubtasks = jest.fn().mockResolvedValue(undefined); + + await modal.initializeFormData(); + + expect((modal as any).tags).toBe('task, existing'); + }); }); describe('TaskEditModal - Tag Mode (Existing Behavior)', () => { @@ -269,5 +279,15 @@ describe('Issue #843: Property Mode Tag Bug', () => { expect(changes.tags).toEqual(['important', 'task']); } }); + + test('should hide task tag on load in tag mode', async () => { + mockTask.tags = ['task', 'existing']; + const modal = new TaskEditModal(mockApp, mockPlugin, { task: mockTask }); + (modal as any).initializeSubtasks = jest.fn().mockResolvedValue(undefined); + + await modal.initializeFormData(); + + expect((modal as any).tags).toBe('existing'); + }); }); }); From f824b503a45bafc81f513ceaa8b71ffc081dd4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Normen=20Mu=CC=88ller?= Date: Sat, 3 Jan 2026 22:37:38 +0100 Subject: [PATCH 3/4] fix: avoid implicit tag removal in edit modal --- src/modals/TaskEditModal.ts | 6 ++++- .../issue-843-property-mode-tag-bug.test.ts | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/modals/TaskEditModal.ts b/src/modals/TaskEditModal.ts index aaa41f3c..a0252dd5 100644 --- a/src/modals/TaskEditModal.ts +++ b/src/modals/TaskEditModal.ts @@ -50,6 +50,7 @@ export class TaskEditModal extends TaskModal { raw: Record; } = { added: [], removed: [], raw: {} }; private unresolvedBlockingEntries: string[] = []; + private initialTags = ""; private isShowingConfirmation = false; private pendingClose = false; @@ -104,6 +105,7 @@ export class TaskEditModal extends TaskModal { ? rawTags.filter((tag) => tag !== this.plugin.settings.taskTag) : rawTags; this.tags = rawTags.length > 0 ? sanitizeTags(visibleTags.join(", ")) : ""; + this.initialTags = this.tags; this.timeEstimate = this.task.timeEstimate || 0; // Handle recurrence @@ -764,6 +766,8 @@ export class TaskEditModal extends TaskModal { } // Parse and compare tags + const tagsUnchanged = + sanitizeTags(this.tags) === sanitizeTags(this.initialTags); const newTags = this.tags .split(",") .map((t) => t.trim()) @@ -780,7 +784,7 @@ export class TaskEditModal extends TaskModal { const oldTags = this.task.tags || []; - if (JSON.stringify(newTags.sort()) !== JSON.stringify(oldTags.sort())) { + if (!tagsUnchanged && JSON.stringify(newTags.sort()) !== JSON.stringify(oldTags.sort())) { changes.tags = newTags.length > 0 ? newTags : undefined; } diff --git a/tests/unit/issues/issue-843-property-mode-tag-bug.test.ts b/tests/unit/issues/issue-843-property-mode-tag-bug.test.ts index 4f0134f4..d5e38929 100644 --- a/tests/unit/issues/issue-843-property-mode-tag-bug.test.ts +++ b/tests/unit/issues/issue-843-property-mode-tag-bug.test.ts @@ -225,6 +225,17 @@ describe('Issue #843: Property Mode Tag Bug', () => { expect((modal as any).tags).toBe('task, existing'); }); + + test('should not mark tags as changed when untouched in property mode', async () => { + mockTask.tags = ['task', 'existing']; + const modal = new TaskEditModal(mockApp, mockPlugin, { task: mockTask }); + (modal as any).initializeSubtasks = jest.fn().mockResolvedValue(undefined); + + await modal.initializeFormData(); + + const changes = (modal as any).getChanges(); + expect(changes.tags).toBeUndefined(); + }); }); describe('TaskEditModal - Tag Mode (Existing Behavior)', () => { @@ -289,5 +300,16 @@ describe('Issue #843: Property Mode Tag Bug', () => { expect((modal as any).tags).toBe('existing'); }); + + test('should not mark tags as changed when untouched in tag mode', async () => { + mockTask.tags = ['task', 'existing']; + const modal = new TaskEditModal(mockApp, mockPlugin, { task: mockTask }); + (modal as any).initializeSubtasks = jest.fn().mockResolvedValue(undefined); + + await modal.initializeFormData(); + + const changes = (modal as any).getChanges(); + expect(changes.tags).toBeUndefined(); + }); }); }); From b6d6493d7fda4622a0059d371fd029e6e302d7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Normen=20Mu=CC=88ller?= Date: Sat, 3 Jan 2026 22:58:15 +0100 Subject: [PATCH 4/4] docs: update PR notes for edit modal tags --- PR.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/PR.md b/PR.md index 268e1c50..2e4ecbd2 100644 --- a/PR.md +++ b/PR.md @@ -17,3 +17,8 @@ Examples (illustrative): - In property-based mode, preserve user/default tags without filtering them out. - Only write `frontmatter.tags` during updates when tags are explicitly provided; otherwise leave tags untouched. - Fixes callumalpass/tasknotes#1391. + +### Edit modal tag safety + +- Property-based identification now keeps all tags visible in the edit modal (only tag-based mode hides the task tag). +- Tags are only updated when the user actually changes the tags field, preventing accidental tag removal on unrelated edits.