Milestones Architecture¶
Milestones are implemented as a feature-layer policy that consumes canonical plugin state and provider outcomes. The module tracks durable progress counters, evaluates unlock rules, and exposes read-only card data for settings rendering.
Four Single Sources Of Truth¶
- Persistent milestone state in settings (1)
- Milestone rule definitions (2)
- Success-only mutation hooks (3)
-
Settings-page presentation flow (4)
-
Stored in
settings.milestones(counters andunlockedAtmaps). - Located in
src/features/milestones/milestones.ts. - Integrated into
src/core/cache/CacheMutationHandler.ts. - Handled by
LazySettingsTab.tsandSettingsTab.tsx.
Data Model¶
Milestone state is stored under settings.milestones:
- counters: Record of numeric counters for global actions, provider-scoped actions, day buckets, and metadata.
- unlockedAt: Record of unlock timestamps keyed by milestone id.
Counters include categories such as:
- action totals by scope
- daily buckets for streak and consistency calculations
- metadata counters for NLP, recurring series, timezone diversity, and lifetime span markers
Tracking Pipeline¶
- Cache mutation executes operation (1)
- Provider attempted through registry (2)
- CacheMutationHandler calls recordMilestoneAction (3)
milestones.tsupdates counters (4)- Unlock evaluation runs (5)
- Persistence via
PluginState.persistData(6) -
New unlocks queued to toast notifier (7)
-
Triggered by user actions like creating or editing events.
- Delegation ensures the underlying source handles the persistence first.
- Only called after confirmed success to avoid false positives.
- Updates both scoped counters and metadata.
- Evaluated against
MILESTONE_DEFINITIONS. - Ensures progress is saved even if Obsidian crashes.
- Immediate user feedback for achievements.
Failure paths and delegated action rollbacks do not commit milestone progress.
Computation Strategy¶
Each milestone definition has a compute function that returns current and target values. Compute functions are deterministic over the current milestone state and selected runtime context where required.
Representative computation types:
- direct totals such as created.total
- provider-aggregated totals across remote or task families
- threshold counts such as number of providers above a target
- day-series analysis for streak and consistency objectives
- live environment checks such as active remote source count and local live event totals
UI Rendering Contract¶
The settings milestones page consumes getMilestoneCards and does not mutate milestone state. Card fields include id, title, description, targetLabel, current, percent, and unlocked.
Sorting contract:
- unlocked cards first
- lexicographic title order inside same unlock group
Footer contract:
- use shared settings footer renderer to preserve consistent layout and behavior
Internationalization Contract¶
All user-facing milestone text is sourced from locale keys. The architecture relies on translation keys in rule definitions and notice keys for unlock toasts.
Extension Checklist¶
To add a new milestone:
- Add a new definition in MILESTONE_DEFINITIONS with id and i18n keys.
- Add or reuse compute primitives in milestones.ts.
- Add translation keys in en locale and required locales.
- Verify increment paths are covered by success-only mutation hooks.
- Validate settings page rendering and toast behavior.