mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
446 lines
23 KiB
Markdown
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`
|