callumalpass_tasknotes/docs/development/data-flow.md
2026-05-19 13:10:40 +10:00

23 KiB

TaskNotes Data Flow

This document records the intended data-flow model for the refactor.

Source of Truth

Task data is stored in Markdown files with frontmatter inside the user vault.

Primary source of truth:

  • markdown file content
  • Obsidian metadata cache for indexed read access

TaskNotes should avoid introducing a competing internal database for core task state.

Settings Startup Path

  1. TaskNotesPlugin asks settingsPersistence to read plugin data.json.
  2. If Obsidian returns null while the existing data file is present, the read is retried before defaults are trusted.
  3. A compromised startup read blocks settings saves for that session so existing user configuration is not overwritten by in-memory defaults.
  4. Legacy settings and nested defaults are normalized in settingsPersistence before TaskNotesPlugin.settings is assigned.
  5. Settings-only saves write the known settings keys back to plugin data while preserving other persisted plugin state already in data.json.

Read Path

  1. Vault files change.
  2. Obsidian metadata cache updates.
  3. TaskManager and related adapters read task state from metadata cache.
  4. Task frontmatter is identified through taskIdentification, then mapped task data is completed through taskInfoAssembly.
  5. Filter/query behavior receives user-field settings, i18n, link resolution, and subtask lookup through the FilterService runtime contract rather than reaching for the full plugin object.
  6. Query predicate evaluation runs through filterPredicateEvaluation, which owns recursive group semantics, user-field coercion, project-link matching, hasSubtasks, and status.isCompleted decisions.
  7. Domain services and views consume normalized TaskInfo representations.

Write Path

  1. A view, modal, command, or API endpoint requests a task mutation.
  2. The request flows through TaskService or a dedicated mutation collaborator.
  3. Single-property updates are normalized and planned through taskPropertyUpdate before frontmatter is written.
  4. Bulk task updates are normalized and planned through taskUpdatePlanning before mapped frontmatter or body writes happen.
  5. Frontmatter/body changes are written to the markdown file.
  6. taskPropertyChangeSideEffects refreshes the task cache, emits task update events, and runs webhook, calendar-sync, and auto-archive side effects.
  7. Obsidian metadata cache emits update events.
  8. Event listeners invalidate caches and refresh views.

Task creation payload assembly should happen before the write boundary:

  • TaskCreationService and TaskUpdateService receive narrow runtime contracts, not the full plugin type, so the task write path documents which settings, app, cache, event, mapper, and sync capabilities it can use.
  • Bases view filters are converted into frontmatter defaults through basesFilterDefaults before the creation payload is assembled.
  • Bases New frontmatter is normalized through basesTaskCreation.
  • Kanban column and swimlane creation defaults are planned through kanbanCreationDefaults before the creation payload is assembled.
  • Modal user fields are normalized through taskModalUserFields.
  • Modal creation defaults and pre-populated values are normalized through taskCreationFormState before TaskCreationModal renders or saves.
  • Modal post-save subtask project writes are planned through taskCreationSubtasks.
  • Modal edit defaults and cached user-field values are normalized through taskEditFormState before TaskEditModal renders.
  • Modal edit writes read existing frontmatter and assemble change input through taskEditChangeState before TaskEditModal calls TaskService.
  • Modal edit subtask additions/removals are planned through taskEditSubtasks before child task project links are updated.
  • NLP/API creation payloads use the shared parsed-data assembly path.
  • Convert-current-note command payloads use currentNoteConversion to coerce existing frontmatter, apply status/priority defaults, and extract the body before opening the edit modal.
  • Task creation defaults are applied through taskCreationDefaults.
  • Task title sanitization for filenames and stored frontmatter values is applied through taskTitleSanitizer.
  • Single-property task updates use taskPropertyUpdate to normalize incoming values and apply frontmatter mutation rules.
  • Bulk task updates use taskUpdatePlanning to sanitize time-entry updates, plan recurrence date/DTSTART changes, apply mapped frontmatter and explicit field-removal rules, merge custom frontmatter, and assemble the returned task state.
  • Archive toggles use taskArchivePlanning to plan archive state, frontmatter tag changes, and optional archive/tasks folder moves.
  • Time-tracking start/stop/delete writes use taskTimeTrackingPlanning to sanitize legacy entries, choose the active entry to close, validate deleted entry indexes, and apply timeEntries frontmatter mutations.
  • Recurring completion/skip writes use taskRecurringPlanning to select the action date, update complete/skipped instance arrays, adjust recurrence anchors, and advance scheduled/due dates before side effects run.
  • Blocking relationship updates use taskBlockingRelationships to deduplicate changed blocked-task paths and plan reverse blockedBy add/remove patches before child tasks are written.
  • Property-change side effects are coordinated through taskPropertyChangeSideEffects after the frontmatter write has happened.

Callers should pass typed task creation data into TaskService; they should not reimplement frontmatter filtering, default application, or user-field coercion.

Cache Principles

Current intent:

  • metadata cache remains the primary read accelerator
  • plugin-local caches should be derived and invalidatable
  • derived caches must never become a second source of truth
  • task-file identification and final TaskInfo assembly stay in focused helpers so cache reads, frontmatter matching, and display defaults are tested independently

Refactor rule:

  • every plugin-local cache should document:
    • owner
    • key
    • invalidation trigger
    • whether stale reads are acceptable

Event Flow

Current event propagation uses plugin/task-manager events plus view-level refresh handling.

Refactor direction:

  • distinguish domain events from UI refresh events
  • normalize event payload types
  • keep low-level file events from leaking too far into UI modules

Bases Boundary

Bases values and entries are not a stable internal domain model. They should be adapted into local TaskNotes types before the rest of the code depends on them.

Refactor rule:

  • use local adapter types for the subset of Bases APIs the plugin actually uses
  • prefer unknown plus narrowing over any
  • keep Bases property mapping dependent on the narrow FieldMapper contract instead of the full plugin object
  • keep Bases-specific value extraction out of general rendering code unless the renderer is explicitly responsible for displaying a native Bases value
  • keep native conversion and display stringification for Bases value wrappers behind basesValueConversion
  • keep cheap Bases entry property assembly from frontmatter/properties and file metadata behind basesEntryProperties
  • keep filter-expression defaults for Bases-created tasks behind basesFilterDefaults, including task-tag exclusion, mapped core fields, custom user-field keys, list properties, and current-file link defaults
  • keep Bases createFileForView task-creation data assembly behind basesCreateFileForView; the view should only open the modal and refresh after creation
  • keep default Base file creation and overwrite policy behind a startup/settings helper rather than inside the plugin shell
  • unregister custom Bases views synchronously during plugin unload so reloads cannot remove newly registered view types after startup
  • keep shared Bases selection-mode classes, selected-card visuals, keyboard shortcuts, click-selection decisions, indicator behavior, and default visible-task path extraction behind basesSelectionUi
  • keep shared Bases copy/export table assembly, value stringification, TSV/CSV escaping, and export file-name sanitization behind basesExport
  • keep shared Bases task update/delete relevance decisions, path-rename refresh planning, and rendered-card path extraction behind basesUpdateEvents
  • keep shared Bases task-update/delete listener registration, relevant-path cache mutation, and listener cleanup behind basesTaskUpdateListeners
  • keep shared Bases config-change refresh hooks, data-update render debouncing, and explicit refresh debouncing behind basesRefreshLifecycle
  • keep shared Bases toolbar DOM integration for the TaskNotes New button behind basesToolbar
  • keep shared Bases result-menu task copy action construction and current-view clipboard task assembly behind basesTaskCopyActions
  • keep shared Bases-to-TaskCard visible property mapping and display-label override assembly behind basesVisibleProperties
  • keep shared Bases search controls, no-results state, and rendering behind basesSearchUi

Current adapter seams include:

  • calendar task, property, and external-event builders
  • calendar mutation planning
  • calendar config snapshot construction for refresh/recreate decisions
  • calendar data-signature property selection and value normalization
  • calendar initial-date and navigation-state planning for recreate decisions
  • Kanban task grouping, column/swimlane ordering, drag planning, and task-creation default planning
  • Task List drag/drop insertion geometry
  • Task List grouped render planning and sub-property grouping
  • Bases value conversion and group-key display strings
  • Bases entry property assembly from frontmatter and file metadata
  • Bases createFileForView task-creation data assembly
  • shared Bases formula evaluation and path-property map assembly
  • shared Bases selection UI state and click/keyboard selection decisions
  • shared Bases copy/export table assembly
  • shared Bases update/delete event relevance planning
  • shared Bases task-update listener lifecycle handling
  • shared Bases refresh lifecycle scheduling
  • shared Bases toolbar New button integration
  • shared Bases result-menu task copy actions
  • shared Bases visible-property and display-label mapping
  • shared Bases search control creation and no-results rendering
  • Bases-backed task creation assembly
  • TaskCard property access
  • TaskCard relationship expansion rendering
  • TaskCard metadata assembly
  • TaskCard metadata-line toggle wiring
  • TaskCard render-state and class assembly
  • TaskCard completion-state visual refresh
  • TaskCard status/priority indicator rendering
  • TaskCard secondary badge and dependency-toggle rendering
  • TaskCard title and link rendering
  • TaskCard context-menu integration
  • TaskCard quick-action service calls
  • TaskCard badge and interactive-control helpers
  • TaskModal details-editor adapter wiring
  • TaskModal title-input behavior
  • TaskModal details/right-column layout transitions
  • TaskModal mobile focus and keyboard scroll guards
  • TaskModal field rendering and visibility dispatch
  • TaskModal metadata field construction
  • TaskModal organization field construction
  • TaskModal project list rendering
  • TaskModal subtask list rendering
  • TaskModal dependency list state and candidate filtering
  • TaskModal task-selector opening for dependency and subtask pickers
  • TaskModal action menu-state snapshots and icon-state assembly
  • TaskCreationModal initial form-state defaults and pre-populated values
  • TaskCreationModal post-save subtask assignment planning
  • TaskEditModal initial form-state defaults and cached user-field values
  • TaskEditModal frontmatter-cache reads and edit-change input assembly
  • TaskEditModal subtask add/remove planning and child project-link updates
  • Bases filter defaults for task creation from filtered Base views
  • default Bases file creation and regeneration writes

Date and Recurrence Flow

Date handling is a historically sensitive area.

Refactor rule:

  • parsing and coercion should happen at the boundary
  • internal logic should use explicit date semantics
  • recurrence expansion should remain centralized rather than duplicated in views
  • date rollover detection should stay behind dateChangeDetection, which owns the minute poll, next-midnight timeout, timer registration, and event trigger

Calendar views should build events through normalized event builders before touching FullCalendar APIs. Drag/drop and resize handlers should ask planning helpers for explicit mutation plans before performing side effects. Event-mount list-card rendering and Bases-backed task enrichment belong in calendarEventMount. Initial-date option normalization, property-backed navigation candidates, and navigation-state snapshots belong in calendarInitialDate; Calendar views keep FullCalendar callback wiring, hover previews, context menus, and refresh orchestration.

Agenda date sections should ask agendaTaskSelection whether a task belongs to a date or overdue section. FilterService keeps task loading and query filtering, while recurrence expansion, completed-overdue hiding, and due/scheduled date eligibility live in the helper.

Task sorting should run through filterTaskSorting. FilterService keeps metadata-cache access and orchestration while the helper owns sort-key comparison, time-aware date ordering, natural fallback ordering, tag ordering, and user-field sort comparison.

Filter predicates should ask filterPredicateEvaluation whether a task matches a query node. FilterService keeps cache reads, query planning, grouping, labels, and option discovery while the helper owns recursive group evaluation, dynamic user-field coercion, project matching, subtask lookup, and completion-state semantics.

Filter option assembly should run through filterOptions. FilterService keeps option caching and invalidation while the helper owns task-folder extraction and dynamic user-property definition construction.

Filter query state assembly should run through filterQueryState. FilterService keeps validation, cache reads, and execution orchestration while the helper owns default query construction, quick-toggle query mutation, and saved-query normalization.

UI State Flow

TaskCard and TaskModal rendering should consume already-normalized state.

Refactor rule:

  • property lookup belongs in access helpers
  • input parsing belongs in modal state helpers
  • relationship filtering/sorting belongs in relationship helpers
  • relationship expansion DOM containers should use injected card renderers
  • visible-property metadata assembly belongs in metadata helpers
  • metadata-line blocked-by toggle wiring belongs in metadata-line helpers
  • target-date, completion, and class-name state belongs in state helpers
  • status-click completion refresh, checkbox sync, title completion sync, and stale status/priority/project class cleanup belong in completion-state helpers
  • status/priority dot visibility, colors, icons, and insertion belong in primary-indicator helpers
  • recurrence, reminder, project, chevron, and dependency badge rendering belongs in secondary-badge helpers
  • title text resolution, link rendering, and completion class sync belong in title helpers
  • context-menu button wiring and native file-menu fallback belong in context-menu helpers
  • quick-action service calls belong in action helpers
  • reusable badge/control DOM behavior belongs in indicator helpers
  • Task List drag/drop segmenting, insertion-slot resolution, and drop-target reconstruction belong in taskListDragGeometry; TaskListView keeps DOM measurement, drag event handling, and persistence
  • Task List grouped-drop frontmatter mutation, status-derived fields, and side-effect task snapshots belong in taskListDropPlanning; TaskListView keeps sort-order queueing, vault writes, notices, and refresh orchestration
  • Task List grouped render items, sub-property groups, grouped sort-scope paths, formula-backed property maps, and group-value stringification belong in taskListGrouping; TaskListView keeps render mode switching, virtualization, and DOM updates
  • custom user-field input/toggle DOM behavior belongs in user-field control helpers
  • creation-modal defaults, pre-populated values, default reminders, project strings, and user-field defaults belong in creation form-state helpers
  • creation-modal subtask project assignment belongs in creation subtask helpers
  • edit-modal defaults, identifying tag filtering, recurrence/reminder copies, and cached user-field values belong in edit form-state helpers
  • edit-modal frontmatter-cache reads and task-change input assembly belong in edit change-state helpers
  • edit-modal subtask add/remove planning and child project-link mutation belong in edit subtask helpers
  • modal action-button bar construction and save disabled-state handling belongs in action-button helpers
  • modal compact action-icon construction and the shared core action-icon set belongs in action-bar helpers
  • modal compact action-icon active-state, tooltip, and color updates belong in action-icon state helpers
  • modal date, status, priority, recurrence, and reminder action-menu callbacks belong in action-menu helpers
  • modal action menu-state snapshots, due/scheduled setter routing, recurrence anchor preservation, and icon-state assembly belong in action-state helpers
  • modal default status/priority and recurrence action-label derivation belong in action-value helpers
  • title textarea construction, newline normalization, Enter-key policy, and dynamic title height belong in title-input helpers
  • details/right-column collapse, expansion, and animation classes belong in layout helpers
  • title-focus scroll restoration, mobile-like environment checks, and mobile keyboard scroll nudges belong in focus-guard helpers
  • details-editor markdown lifecycle, tab focus policy, and value sync belong in details-editor helpers
  • modal field grouping, core-field dispatch, and user-field fallback belong in field-rendering helpers
  • contexts, tags, and time-estimate field construction belongs in metadata field helpers
  • project, subtask, and dependency list-field shells belong in organization field helpers
  • project-string parsing, project item deduplication, project list rendering, and project value serialization belong in project state helpers
  • subtask candidate filtering, selected-subtask deduplication, removal, and cached/fallback list rendering belong in subtask state helpers
  • dependency duplicate checks, add/remove behavior, and blocked-by/blocking candidate filtering belong in dependency state helpers
  • task-selector task loading, empty-state notices, cancellation handling, and selector failure logging belong in the task-selector helper; modals keep the selected-item state mutation
  • filename-safe and storage-safe task title normalization belongs in taskTitleSanitizer; callers should not strip filename-unsafe characters or control characters inline
  • single-property normalization and frontmatter mutation rules belong in taskPropertyUpdate; callers should not duplicate status coercion, completed-date derivation, date removal, dependency serialization, or date-modified writes inline
  • bulk task-update sanitation, recurrence-update planning, mapped frontmatter mutation, custom-frontmatter merge/delete semantics, explicit mapped-field removals, and returned task-state assembly belong in taskUpdatePlanning; TaskUpdateService keeps vault writes, renames, body writes, cache updates, events, webhooks, calendar sync, and auto-archive side effects
  • archive tag mutation and archive/tasks move path construction belong in taskArchivePlanning; TaskService keeps the vault write, rename, cache, calendar, and webhook side effects
  • time-entry sanitization and start/stop/delete frontmatter mutation rules belong in taskTimeTrackingPlanning; TaskService keeps active-session checks, vault writes, cache updates, events, and webhooks
  • recurring instance action-date selection, complete/skipped array mutation, DTSTART updates, and next-occurrence advancement belong in taskRecurringPlanning; TaskService keeps vault writes, body checkbox resets, cache updates, events, webhooks, and calendar sync
  • blocking relationship path deduplication, reverse blockedBy mutation, and dependency metadata preservation belong in taskBlockingRelationships; TaskService keeps task cache reads and child task writes
  • post-write cache refresh, dependent-task refresh events, webhooks, calendar sync, and auto-archive routing belong in taskPropertyChangeSideEffects
  • Calendar drag/resize frontmatter mutation decisions belong in calendarMutationPlanning; Calendar views keep event dispatch, vault file lookup, and provider/task-service side effects
  • Kanban drop frontmatter mutation, status-derived fields, and post-write side-effect task snapshots belong in kanbanDragUtils; Kanban final coordinate drop-target reconstruction and optimistic DOM card movement also belong there. Kanban column/swimlane task-creation defaults belong in kanbanCreationDefaults, while Kanban views keep event wiring, active drag state, sort-order service calls, vault writes, notices, and modal entry points
  • task-frontmatter identification belongs in taskIdentification; callers should not duplicate tag/property matching, metadata-cache # stripping, or boolean-like identifier comparison
  • mapped task-info defaults, computed tracked time, and blocking flags belong in taskInfoAssembly; TaskManager keeps metadata-cache access, FieldMapper calls, and dependency-cache lookups
  • custom user-field filter coercion, list-token normalization, sort comparison, and group bucket selection belong in userFieldValues; FilterService keeps metadata-cache reads and query orchestration
  • filter predicate evaluation belongs in filterPredicateEvaluation; callers should not duplicate recursive group rules, user-field filter coercion, project wikilink matching, hasSubtasks, or status.isCompleted semantics
  • task grouping, project/tag fan-out, date bucket labels, completed-date labels, and group header ordering belong in filterTaskGrouping; FilterService injects translation, locale, status, priority, project-resolution, and frontmatter-value access
  • task sort-key comparison, time-aware date ordering, fallback ordering, tag ordering, and user-field sort comparison belong in filterTaskSorting; FilterService injects status, priority, and frontmatter-value access
  • filter option assembly, task-folder extraction, and dynamic user-property definitions belong in filterOptions; FilterService keeps cache invalidation and source orchestration
  • default query construction, quick-toggle condition mutation, and partial query normalization belong in filterQueryState
  • DOM functions should wire controls, render values, and call services

This keeps DOM tests focused on interaction while pure unit tests cover state rules such as custom field filtering, list parsing, dependency normalization, and inherited view ordering.

Operational Principle

When debugging a behavior problem, the first question should be:

"Is this a source-data issue, an adapter issue, a domain-logic issue, or a view-refresh issue?"

The architecture should make that answer obvious. New diagnostics should use TaskNotesLogCategory values so logs preserve the same distinction:

  • source-data problems usually map to validation or stale-data
  • adapter/API problems usually map to provider or configuration
  • write-path failures usually map to persistence
  • unexpected view/service failures usually map to internal