Skip to content

Conversation

@renatomen
Copy link
Contributor

@renatomen renatomen commented Oct 17, 2025

Description

Fixes #953 - Prevents non-task notes from being incorrectly identified as subtasks and ensures views update instantly when task identification changes.

Root Causes

  1. MinimalNativeCache.getTaskInfo() returned TaskInfo for any file with frontmatter, without validating task identification criteria
  2. ProjectSubtasksService.buildProjectIndex() marked tasks as projects based on links from non-task files
  3. Cache invalidation events weren't firing when files stopped being tasks (early exit prevented event emission)
  4. ProjectSubtasksService had no event listeners for metadata changes, relying only on 30-second TTL
  5. Cache events weren't propagating to plugin emitter - MinimalNativeCache triggered file-updated events but EVENT_DATA_CHANGED was never fired, so views didn't refresh
  6. Task validation missing in extraction - extractTaskInfoFromNative() extracted TaskInfo without validating task status, causing asymmetric behavior (task→note vs note→task transitions)

Solution

Task Identification Validation

  • Added isTaskFile() validation to getTaskInfo() method
  • Made isTaskFile() public for use by ProjectSubtasksService
  • Added isTaskFile() validation to buildProjectIndex() method
  • Created comprehensive test suite with 13 test cases

Event-Driven Cache Invalidation

  • Added initialize() method to ProjectSubtasksService with event listeners
  • Track files that were previously tasks to ensure events fire on task→non-task transitions
  • Clean up cached data when files stop being tasks
  • Invalidate project index immediately on metadata changes

Test Mock Fixes for CI

  • Fixed test failures in GitHub Actions by properly mocking TFile and dependencies
  • Created mockFile using Object.create(TFile.prototype) to pass instanceof checks
  • Added missing parameters and methods to mocks

Cache Event Bridge & Task Validation

  • Cache Event Bridge (main.ts): Added setupCacheEventBridge() method to propagate cache events (file-updated, file-deleted, file-renamed) to plugin emitter's EVENT_DATA_CHANGED, ensuring views refresh when cache changes
  • Task Validation (MinimalNativeCache.ts): Added isTaskFile() validation to extractTaskInfoFromNative() to ensure it returns null when file stops being a task, preventing early exit in comparison logic
  • Uses requestAnimationFrame for efficient UI updates
  • Fixes asymmetric behavior where views updated instantly when note→task but not when task→note

Impact

  • Non-task notes no longer appear as subtasks
  • Chevron only appears when actual task subtasks exist
  • Views refresh immediately when task identification changes (both tag-based and property-based)
  • Works in both directions: note→task AND task→note transitions
  • No need to wait 30 seconds or restart Obsidian for changes to take effect
  • No breaking changes - only corrects invalid behavior and completes missing event flow

Testing

  • 13 unit tests for task identification validation (tag-based, property-based, edge cases)
  • 5 unit tests for cache event bridge functionality
  • All MinimalNativeCache tests passing
  • All cache event bridge tests passing
  • Verified non-task notes don't appear as subtasks
  • Verified instant view updates when task identification changes

Files Modified

  • src/utils/MinimalNativeCache.ts - Task validation in getTaskInfo() and extractTaskInfoFromNative()
  • src/services/ProjectSubtasksService.ts - Event listeners and cache invalidation
  • src/main.ts - Cache event bridge setup
  • tests/unit/utils/MinimalNativeCache.getTaskInfo.test.ts - Test suite (13 tests)
  • tests/unit/main.cacheEventBridge.test.ts - New test file (5 tests)

…alpass#953)

Add missing isTaskFile() validation to getTaskInfo() method to ensure
only files meeting task identification criteria return TaskInfo objects.

Root Cause:
- getTaskInfo() was returning TaskInfo for ANY file with frontmatter
- Missing validation check that was present in getCachedTaskInfoSync()
- Bug existed since MinimalNativeCache creation (commit 96a3f15)

Changes:
- Add isTaskFile() check to MinimalNativeCache.getTaskInfo()
- Create comprehensive test suite for task identification validation
- Ensure consistency between async and sync task info methods

Impact:
- Notes with Parent/project properties but no task tag/property no longer
  appear as subtasks
- Fixes incorrect subtask display reported in issue callumalpass#953
- No breaking changes - only corrects invalid behavior

Testing:
- Added 13 test cases covering tag-based, property-based, and edge cases
- Issue callumalpass#953 specific scenarios verified passing
…ass#953)

Fix project index not updating when task tags are removed from notes.

Root Cause:
- MinimalNativeCache early-exited for non-task files, preventing
  "file-updated" event from firing when a file stopped being a task
- ProjectSubtasksService had no event listeners to invalidate index
  on metadata changes, relying only on 30-second TTL

Changes:
- Add initialize() method to ProjectSubtasksService with event listeners
  for file-updated, file-deleted, and file-renamed events
- Track files that were previously tasks in handleFileChangedDebounced()
  to ensure events fire when files transition from task to non-task
- Clean up cached data (lastKnownTaskInfo, lastKnownFrontmatter) when
  files stop being tasks
- Call projectSubtasksService.initialize() in main.ts during startup

Impact:
- Chevron state now updates instantly when task tags are added/removed
- No need to wait 30 seconds or restart Obsidian for changes to reflect
- Project index invalidates immediately on any metadata change

Files Modified:
- src/services/ProjectSubtasksService.ts (initialize, invalidateIndex)
- src/utils/MinimalNativeCache.ts (handleFileChangedDebounced, handleFileChanged)
- src/main.ts (initialization call)
Fix test failures in GitHub Actions by properly mocking TFile and dependencies.

Root Cause:
- mockFile wasn't passing instanceof TFile check (plain object)
- mockFieldMapper missing storeTitleInFilename parameter
- mockApp.metadataCache missing getFirstLinkpathDest method

Changes:
- Create mockFile using Object.create(TFile.prototype) with Object.defineProperty
- Add storeTitleInFilename parameter to mockFieldMapper.mapFromFrontmatter
- Add getFirstLinkpathDest method to mockApp.metadataCache

Test Results:
- All 13 tests passing locally
- No changes to production code - only test fixes
…allumalpass#953)

Fix issue where views didn't update immediately when property-based task
identification changed. Required two fixes to complete the event flow:

1. Cache Event Bridge (main.ts):
   - Added setupCacheEventBridge() method to propagate cache events to plugin emitter
   - Bridges file-updated, file-deleted, and file-renamed events to EVENT_DATA_CHANGED
   - Ensures views listening to EVENT_DATA_CHANGED refresh when cache changes
   - Uses requestAnimationFrame for efficient UI updates

2. Task Validation (MinimalNativeCache.ts):
   - Added isTaskFile() validation to extractTaskInfoFromNative()
   - Ensures method returns null when file stops being a task
   - Prevents early exit in handleFileChangedDebounced when task becomes non-task
   - Fixes asymmetric behavior (task→note vs note→task transitions)

Root Cause:
- MinimalNativeCache triggered file-updated events but plugin's EVENT_DATA_CHANGED was never fired
- extractTaskInfoFromNative() extracted TaskInfo without validating task status
- When isTask changed from true to false, taskInfoEquals() returned true (same TaskInfo)
- This caused early exit, preventing event propagation to views

Impact:
- Views now refresh immediately when task identification properties change
- Works for both tag-based and property-based identification
- Fixes both directions: note→task and task→note transitions
- No breaking changes, only completes missing event flow

Test Results:
- All MinimalNativeCache tests passing (33/33)
- New cache event bridge tests passing (5/5)
- Fixes tests that failed in GitHub Actions CI
- No new test failures introduced

Files Modified:
- src/main.ts: Added setupCacheEventBridge() method (38 lines)
- src/utils/MinimalNativeCache.ts: Added task validation (4 lines)
- tests/unit/main.cacheEventBridge.test.ts: New test file (5 tests)
@renatomen renatomen marked this pull request as ready for review October 17, 2025 10:43
@callumalpass
Copy link
Owner

Thanks for this fix for #953, @renatomen ! However, I'm worried the event-driven index invalidation will cause performance issues. Every file edit triggers a full subtask index rebuild (scanning all files), which can take 1-2 seconds in large vaults. The subtask index rebuilding can be very slow, which is why it is only allowed every 30 seconds.

I think I might look into a different fix for #953 in v4. Basically, I think I'll add a view option that allows users to select whether filter conditions flow down into subtasks. This should allow the appropriate filtering of non-task notes from the subtasks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: All TaskNotes Views include subtasks that don't match the "Task Identification" settings

2 participants