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

446 lines
23 KiB
Markdown

# 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`