From 771cf9c0cb446d373a8a83884d78d3b0210e6032 Mon Sep 17 00:00:00 2001 From: Dusk Date: Sun, 26 Apr 2026 23:16:19 +0800 Subject: [PATCH] docs(audit): review plugin improvement opportunities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 中文: 基于真实代码审查整理插件可完善方向、风险点和后续开发优先级。 English: Reviews plugin improvement opportunities, risks, and prioritized next steps based on real code inspection. --- ...ugin-improvement-audit-architecture-map.md | 257 +++++++ docs/plugin-improvement-audit-report.md | 632 ++++++++++++++++++ docs/plugin-improvement-next-prompts.md | 346 ++++++++++ 3 files changed, 1235 insertions(+) create mode 100644 docs/plugin-improvement-audit-architecture-map.md create mode 100644 docs/plugin-improvement-audit-report.md create mode 100644 docs/plugin-improvement-next-prompts.md diff --git a/docs/plugin-improvement-audit-architecture-map.md b/docs/plugin-improvement-audit-architecture-map.md new file mode 100644 index 0000000..be78b97 --- /dev/null +++ b/docs/plugin-improvement-audit-architecture-map.md @@ -0,0 +1,257 @@ +# Plugin Improvement Audit Architecture Map + +## Scope + +This map is based on the current production runtime path in the repository as inspected on 2026-04-26. It focuses on active code, active wiring, and code paths that materially affect user-visible sync behavior. + +## 1. Active Entry Points + +### Runtime entry + +- `main.ts` + - Exports `src/presentation/AnkiHeadingSyncPlugin.ts` as the plugin entry. + +### Plugin composition root + +- `src/presentation/AnkiHeadingSyncPlugin.ts :: onload()` + - Creates persistence adapters: + - `src/infrastructure/obsidian/ObsidianPluginDataStore.ts` + - `src/infrastructure/persistence/DataJsonPluginConfigRepository.ts` + - `src/infrastructure/persistence/DataJsonPluginStateRepository.ts` + - Creates external gateways: + - `src/infrastructure/anki/AnkiConnectGateway.ts` + - `src/infrastructure/obsidian/ObsidianVaultGateway.ts` + - Creates main use cases: + - `ManualSyncCurrentFileUseCase` + - `ManualSyncVaultUseCase` + - `RebuildCardIndexUseCase` + - `ClearCurrentFileSyncedCardsUseCase` + - `CleanupEmptyDecksUseCase` + - Registers commands through `src/presentation/commands/registerCommands.ts` + - Registers the settings UI through `src/presentation/settings/PluginSettingTab.ts` + +### User-visible command surface + +- `src/presentation/commands/registerCommands.ts` + - Registers 4 commands: + - sync current file + - sync vault + - clear current file synced cards + - cleanup empty decks + +### User-visible settings surface + +- `src/presentation/settings/PluginSettingTab.ts` + - Active cards: + - card types + - sync content + - deck + - scope + - commands + +## 2. Active Runtime Layers + +### Presentation + +- `src/presentation/AnkiHeadingSyncPlugin.ts` + - Plugin lifecycle, command callbacks, notices, settings update entry. +- `src/presentation/settings/PluginSettingTab.ts` + - Settings rendering, note-type cache loading, field-mapping editing, folder-scope editing, deck template insertion button. +- `src/presentation/notices/NoticeService.ts` + - User-facing success/error summaries. +- `src/presentation/modals/EmptyDeckSelectionModal.ts` + - Deck cleanup modal. +- `src/presentation/i18n/*` + - Locale detection and message lookup. + +### Application + +- `src/application/use-cases/*` + - Thin user-action entry points over services. +- `src/application/services/ManualSyncService.ts` + - Main orchestration service for file sync, vault sync, and rebuild index. +- `src/application/services/FileIndexerService.ts` + - Scope filtering, incremental scan decisions, state-assisted restore of unchanged cards. +- `src/application/services/AnkiBatchExecutor.ts` + - Batched Anki note create/update/model-migrate/tag-sync/deck-change/media-upload. +- `src/application/services/QaGroupSyncService.ts` + - QA Group note sync, slot preservation, model mismatch handling, GI marker generation. +- `src/application/services/MarkdownWriteBackService.ts` + - File-grouped marker writes, conflict capture, pending write-back generation. +- `src/application/services/RenderConfigService.ts` + - Deck resolution, note-model selection, render-config hashing. +- `src/application/services/NoteFieldMappingService.ts` + - Saved mapping validation and rendered-field projection. +- `src/application/services/QaGroupFieldMappingService.ts` + - QA Group slot derivation and validation. + +### Domain + +- `src/domain/manual-sync/services/CardIndexingService.ts` + - Heading scanning, card-type detection, ID/GI marker recovery, QA Group and semantic QA parsing. +- `src/domain/manual-sync/services/DiffPlannerService.ts` + - Create/update/change-deck/rewrite-marker/orphan planning. +- `src/domain/manual-sync/services/CardMarkerService.ts` + - Card ID marker parse/create/validate/apply. +- `src/domain/manual-sync/services/GroupMarkerService.ts` + - GI marker parse/create/validate/apply. +- `src/domain/manual-sync/services/DeckExtractionService.ts` + - YAML/body deck declaration parsing. +- `src/domain/manual-sync/services/DeckResolutionService.ts` + - Folder/file/default deck resolution and warning generation. +- `src/domain/manual-sync/services/QaGroupBlockParser.ts` + - QA Group block parsing. +- `src/domain/manual-sync/services/SemanticQaListParser.ts` + - Semantic QA list parsing. + +### Infrastructure + +- `src/infrastructure/anki/AnkiConnectGateway.ts` + - AnkiConnect HTTP adapter and `multi` request batching. +- `src/infrastructure/obsidian/ObsidianVaultGateway.ts` + - Vault reads, compare-and-swap writes, folder tree, backlinks, media resolution. +- `src/infrastructure/persistence/DataJsonPluginConfigRepository.ts` + - Settings load/save normalization and validation. +- `src/infrastructure/persistence/DataJsonPluginStateRepository.ts` + - Plugin state load/save and migration. + +## 3. Main Data Flow + +### Manual sync: current file + +1. Command callback in `registerCommands()` calls `AnkiHeadingSyncPlugin.runSyncCurrentFile()`. +2. `ManualSyncCurrentFileUseCase.execute()` forwards to `ManualSyncService.syncFile()`. +3. `ManualSyncService.syncFile()` validates settings and scope, then loads plugin state. +4. `FileIndexerService.indexFile()` reads the target Markdown file and delegates parsing to `CardIndexingService.index()`. +5. `DiffPlannerService.plan()` compares indexed cards with persisted state. +6. `AnkiBatchExecutor.execute()` syncs ordinary/basic/cloze/semantic cards. +7. `QaGroupSyncService.sync()` syncs QA Group blocks separately. +8. `MarkdownWriteBackService.write()` writes card ID markers and GI markers back into Markdown. +9. `ManualSyncService.buildNextState()` saves files/cards/groupBlocks/pending write-back into plugin state. +10. `NoticeService.showSyncSummary()` renders a localized summary. + +### Manual sync: vault + +1. `ManualSyncVaultUseCase.execute()` forwards to `ManualSyncService.syncVault()`. +2. `FileIndexerService.indexVault()` uses `ScanScopeService` plus `ObsidianVaultGateway.listMarkdownFileRefs()` to enumerate in-scope files. +3. Incremental decisions are made per file using file stamp, deck-rule fingerprint, missing known cards, and pending write-back state. +4. Remaining flow matches current-file sync. + +### Rebuild index + +1. `RebuildCardIndexUseCase.execute()` forwards to `ManualSyncService.rebuildIndex()`. +2. Vault is reindexed with `forceReadAll = true`. +3. No Anki note create/update occurs. +4. Existing markers can still be rewritten if enough identity exists. +5. Persisted state is rebuilt from indexed files plus recovered QA Group state. + +### Clear current file synced cards + +1. `ClearCurrentFileSyncedCardsUseCase.hasTrackedCards()` checks local state for current file. +2. `execute()` loads note IDs/group IDs from local state. +3. Anki notes are deleted through `AnkiGateway.deleteNotes()`. +4. Markdown markers are removed through `MarkdownSyncedMarkerRemovalService`. +5. Local state is rewritten without that file’s cards/groups. + +## 4. Critical State Objects + +### Settings + +- `src/application/config/PluginSettings.ts :: PluginSettings` + - Controls note-type selection, field mapping caches, deck logic, scope logic, backlink behavior, tag sync, and AnkiConnect URL. + - Contains both current `cardTypeConfigs` and legacy mirrored note-type fields. + +### Persisted plugin state + +- `src/domain/manual-sync/entities/PluginState.ts :: PluginState` + - `files` + - Per-file index metadata: `fileHash`, `fileStamp`, `deckRulesFingerprint`, `noteIds`, `groupIds`. + - `cards` + - Per-note synced card snapshot keyed by note ID. + - `groupBlocks` + - Per-QA-group synced snapshot keyed by group ID. + - `pendingWriteBack` + - Deferred marker writes intended for conflict recovery. + +### Scan-time objects + +- `IndexedCard` +- `IndexedGroupCardBlock` +- `IndexedFile` + +### Plan-time objects + +- `src/domain/manual-sync/value-objects/ManualSyncPlan.ts :: ManualSyncPlan` + - `toCreate` + - `toUpdate` + - `toVerifyDeck` + - `toChangeDeck` + - `toRewriteMarker` + - `toOrphan` + +## 5. Main External API Boundaries + +### Obsidian APIs + +- `Plugin.loadData()` / `Plugin.saveData()` through `ObsidianPluginDataStore` +- Vault file enumeration through `ObsidianVaultGateway.listMarkdownFileRefs()` +- Compare-and-swap write boundary through `ObsidianVaultGateway.replaceMarkdownFile()` +- Metadata and tag extraction through `getAllTags()` and cached metadata +- Obsidian notices and settings controls in presentation layer + +### AnkiConnect + +- All network calls are centralized in `AnkiConnectGateway.invoke()` / `invokeMulti()`. +- The adapter uses AnkiConnect action names directly (`modelNames`, `deckNamesAndIds`, `getDeckStats`, `addNote`, `updateNoteFields`, `changeDeck`, `deleteDecks`, `storeMediaFile`, `multi`, etc.). + +## 6. Active Code vs Likely Legacy / Dormant Remnants + +### Clearly active + +- `main.ts` +- `src/presentation/AnkiHeadingSyncPlugin.ts` +- `src/presentation/settings/PluginSettingTab.ts` +- `src/application/services/ManualSyncService.ts` +- `src/application/services/FileIndexerService.ts` +- `src/application/services/AnkiBatchExecutor.ts` +- `src/application/services/QaGroupSyncService.ts` +- `src/infrastructure/anki/AnkiConnectGateway.ts` +- `src/infrastructure/obsidian/ObsidianVaultGateway.ts` + +### Generated or packaging artifacts + +- `main.js` + - Root-level generated bundle copy written by `esbuild.config.mjs`. +- `dist/plugin/*` + - Staged package output used for release/manual sync. + +### Debug / manual inspection scripts + +- `query_note.js` +- `query_notes.js` + - Direct HTTP scripts with hard-coded note IDs and no repository call sites. + +### Likely dormant or legacy architectural branches + +- `src/application/services/QaGroupModelService.ts` +- `src/application/services/QaGroupModelDefinition.ts` +- `src/application/config/ManagedNoteModels.ts` + - These define and test a managed `ObsiAnki QA Group 12` model, but no production runtime call site invokes `QaGroupModelService.ensureModel()`. + - The active runtime QA Group path instead uses user-selected note types plus `QaGroupFieldMappingService`. + +- `src/presentation/AnkiHeadingSyncPlugin.ts :: runRebuildCardIndex()` + - Wired in plugin initialization, but not surfaced by `registerCommands()` and not listed in the settings command card. + +- Legacy mirrored settings fields in `PluginSettings` + - `qaHeadingLevel`, `clozeHeadingLevel`, `qaGroupMarker`, `qaNoteType`, `clozeNoteType`, `semanticQaMarker`, `semanticQaNoteType` + - Still exist and are derived from `cardTypeConfigs`, which indicates an unfinished migration rather than a clean single source of truth. + +## 7. Audit Notes + +- The active sync engine is stateful and layered. +- The highest-value audit attention should stay on: + - pending write-back persistence + - QA Group authority split + - semantic QA runtime/UI parity + - AnkiConnect failure handling + - hidden rebuild/recovery surface \ No newline at end of file diff --git a/docs/plugin-improvement-audit-report.md b/docs/plugin-improvement-audit-report.md new file mode 100644 index 0000000..8e87428 --- /dev/null +++ b/docs/plugin-improvement-audit-report.md @@ -0,0 +1,632 @@ +# Plugin Improvement Audit Report + +## 1. Executive summary + +This plugin is in a solid middle stage: the core sync engine is better structured than many Obsidian plugins, but product surface and failure recovery are still uneven. + +- Overall maturity + - Medium to high for core sync architecture. + - Medium for user-facing completeness. + - Medium-low for failure recovery resilience across restarts and AnkiConnect outages. + +- Strongest parts + - Layered runtime composition. + - State-aware incremental indexing. + - Explicit marker conflict detection instead of blind overwrite. + - Rich parser and sync-path test coverage for core card types. + +- Highest-risk parts + - Pending write-back recovery is discarded on load. + - Destructive reset paths and batch sync are not transaction-safe. + - Runtime feature surface and settings surface are not aligned. + - QA Group note-model strategy is split between active user-model sync and dormant managed-model code. + +- Next 3 most valuable improvements + 1. Persist `pendingWriteBack` correctly across plugin reloads and add restart-recovery tests. + 2. Add explicit AnkiConnect timeout/health-check handling and friendlier offline UX. + 3. Unify runtime product authority: semantic QA visibility, QA Group model strategy, and note-type source of truth. + +- Validation baseline observed during this audit run + - `git status --short`: clean. + - `npm run lint`: passed. + - `npm test`: failing in current baseline; failures cluster in `FileIndexerService`, `ManualSyncService`, and `ClearCurrentFileSyncedCardsUseCase` tests. + +## 2. Architecture map + +See [plugin-improvement-audit-architecture-map.md](plugin-improvement-audit-architecture-map.md). + +In short, the active runtime path is: + +`main.ts` -> `AnkiHeadingSyncPlugin.onload()` -> use cases -> `ManualSyncService` / cleanup use cases -> domain services + infrastructure gateways -> persisted plugin state + markdown write-back. + +## 3. Confirmed strengths + +### 3.1 Clear runtime composition and layer boundaries + +- Evidence + - `src/presentation/AnkiHeadingSyncPlugin.ts :: onload()` wires repositories, gateways, use cases, and the setting tab in one place. + - Domain parsing and marker logic live outside presentation code. + - AnkiConnect calls are centralized in `src/infrastructure/anki/AnkiConnectGateway.ts`. +- Why this is strong + - It keeps plugin lifecycle, orchestration, parsing, and I/O mostly separated. + - It makes future refactors possible without rewriting the whole plugin. + +### 3.2 Incremental indexing is state-aware, not brute-force only + +- Evidence + - `src/application/services/FileIndexerService.ts :: indexRefs()` skips rereads when `fileStamp`, `deckRulesFingerprint`, and known-card presence still match. + - The same path restores cards and group blocks from persisted state for unchanged files. +- Why this is strong + - Large-vault behavior is already being treated as an engineering problem. + - Re-indexing work is avoided when nothing relevant changed. + +### 3.3 Marker write-back uses optimistic concurrency instead of blind overwrite + +- Evidence + - `src/infrastructure/obsidian/ObsidianVaultGateway.ts :: replaceMarkdownFile()` reads current content and throws `MarkdownWriteConflictError` if it no longer matches the scanned content. + - `src/application/services/MarkdownWriteBackService.ts :: write()` collects `conflictFiles` and `pendingEntries` instead of silently overwriting. +- Why this is strong + - This prevents silent file corruption when a note changes after scan but before marker write-back. + +### 3.4 Card and QA Group identity recovery paths are explicit + +- Evidence + - `src/domain/manual-sync/services/CardIndexingService.ts :: resolveIdentity()` recovers by marker, by state, and by pending write-back. + - `src/domain/manual-sync/services/CardIndexingService.ts :: resolveGroupIdentity()` recovers QA Group notes by GI marker, by `src`, or by block hash. +- Why this is strong + - Sync identity is not tied to a single fragile marker path. + - The design anticipates marker deletion and partial state recovery. + +### 3.5 Saved field mappings are validated against live model fields + +- Evidence + - `src/application/services/NoteFieldMappingService.ts :: validateMapping()` rejects missing or stale fields. + - `src/application/services/QaGroupFieldMappingService.ts :: validateMapping()` rejects missing QA Group fields, duplicate usage, and stale slot references. +- Why this is strong + - The plugin does not blindly trust stale settings. + - Model drift is surfaced as a user-facing problem instead of silently writing wrong fields. + +### 3.6 User-facing error and i18n plumbing is centralized + +- Evidence + - `src/application/errors/PluginUserError.ts` separates internal errors from localized user messages. + - `src/presentation/notices/NoticeService.ts` consolidates summary rendering. + - `src/presentation/i18n/index.ts` and `src/presentation/i18n/messages/*` provide a real translation dictionary instead of ad hoc strings. +- Why this is strong + - Error surfaces are at least architecturally prepared for consistent UX. + +### 3.7 Core sync modules have non-trivial targeted tests + +- Evidence + - `src/application/services/ManualSyncService.test.ts` + - `src/application/services/QaGroupSyncService.test.ts` + - `src/infrastructure/anki/AnkiConnectGateway.test.ts` + - `src/presentation/settings/PluginSettingTab.test.ts` +- Why this is strong + - The repository is not relying only on smoke tests or manual validation. + +## 4. Confirmed problems + +### 4.1 Pending write-back recovery is discarded on every load + +- Severity: high +- Area: sync correctness +- Evidence from real code + - `src/application/services/MarkdownWriteBackService.ts :: write()` creates `pendingEntries` when marker write-back conflicts or fails. + - `src/application/services/FileIndexerService.ts :: indexRefs()` explicitly consumes `state.pendingWriteBack` to force reread/recovery. + - `src/infrastructure/persistence/DataJsonPluginStateRepository.ts :: migratePluginState()` always returns `pendingWriteBack: []`, even when loading an existing `PluginState`. + - `src/infrastructure/persistence/DataJsonPluginStateRepository.test.ts` currently asserts this behavior by expecting `loaded.pendingWriteBack` to equal `[]`. +- Why it matters + - The codebase already models deferred marker recovery, but restart/load drops that state. + - A user who hits a write conflict, closes Obsidian, then retries loses the recovery queue. +- Recommended fix + - Preserve modern `pendingWriteBack` entries on load. + - Keep legacy migration handling separate from modern-state passthrough. + - Add explicit schema-versioned migration logic if needed. +- Estimated implementation size: M +- Risk level: medium +- Suggested test coverage + - Persist and reload modern `pendingWriteBack` entries unchanged. + - Restart after write conflict should still re-attempt marker repair. + +### 4.2 Semantic QA is active in runtime but hidden in settings + +- Severity: high +- Area: UX +- Evidence from real code + - `src/application/config/PluginSettings.ts` includes `semantic-qa` in `CARD_TYPE_CONFIG_IDS` and requires `semanticQaNoteType`. + - `src/domain/manual-sync/services/CardIndexingService.ts` actively parses semantic QA blocks. + - `src/application/services/RenderConfigService.ts :: resolve()` actively selects a semantic QA note type. + - `src/presentation/settings/PluginSettingTab.ts` hardcodes `VISIBLE_CARD_TYPE_CONFIG_IDS = ["basic", "qa-group", "cloze"]`. + - `src/presentation/settings/PluginSettingTab.test.ts` explicitly expects semantic QA controls not to exist. +- Why it matters + - Users can hit runtime semantic QA behavior without being able to fully configure, disable, or map it from the settings page. + - This is a product-surface mismatch, not just a code-style issue. +- Recommended fix + - Either surface semantic QA as a first-class configurable card type, or remove/disable the parser until a proper UI exists. +- Estimated implementation size: M +- Risk level: medium +- Suggested test coverage + - Settings UI coverage for semantic QA controls. + - End-to-end semantic QA sync with explicit note-type and field-mapping selection. + +### 4.3 Rebuild index exists but is not exposed to users + +- Severity: medium +- Area: UX +- Evidence from real code + - `src/presentation/AnkiHeadingSyncPlugin.ts :: onload()` instantiates `RebuildCardIndexUseCase`. + - `src/presentation/AnkiHeadingSyncPlugin.ts :: runRebuildCardIndex()` implements user-facing handling and notices. + - `src/presentation/commands/registerCommands.ts` registers only 4 commands and does not register rebuild. + - `src/presentation/settings/PluginSettingTab.ts :: renderCommandsCard()` lists only the 4 registered commands. +- Why it matters + - A recovery-oriented feature exists in code but is unavailable in normal user flows. + - That increases support burden and leaves state-recovery capability effectively dormant. +- Recommended fix + - Add a command and a clearly labeled UI entry for rebuild index. + - Document what rebuild does and does not do. +- Estimated implementation size: S +- Risk level: low +- Suggested test coverage + - Command registration test. + - Settings/UI test for rebuild trigger visibility. + +### 4.4 QA Group model authority is split between active user-model sync and dormant managed-model code + +- Severity: medium +- Area: architecture +- Evidence from real code + - Active runtime QA Group sync uses `src/application/services/QaGroupSyncService.ts` plus `settings.cardTypeConfigs["qa-group"].noteType` and `QaGroupFieldMappingService`. + - `src/application/services/QaGroupModelService.ts`, `src/application/services/QaGroupModelDefinition.ts`, and `src/application/config/ManagedNoteModels.ts` implement managed model creation for `ObsiAnki QA Group 12`. + - Repository-wide search shows `QaGroupModelService` is only referenced by its own tests, not by production runtime. + - `src/presentation/i18n/messages/en.ts` and `zh.ts` still describe QA Group as syncing into `ObsiAnki QA Group 12`. +- Why it matters + - The repository currently carries two different product stories for QA Group. + - That makes maintenance, documentation, and future migration decisions harder than necessary. +- Recommended fix + - Decide one authority model: + - fully user-selected QA Group models, or + - plugin-managed QA Group model. + - Remove or archive the losing branch. + - Update settings copy accordingly. +- Estimated implementation size: M +- Risk level: medium +- Suggested test coverage + - Remove dead branch tests or convert them into migration tests. + - One integration path from settings to sync for the chosen authority model. + +### 4.5 Settings UI is carrying too much stateful business logic in one file + +- Severity: medium +- Area: architecture +- Evidence from real code + - `src/presentation/settings/PluginSettingTab.ts` is roughly 1900 lines and owns: + - note type cache loading + - field cache hydration + - QA Group derivation logic + - debounced persistence + - folder tree load/cache/expansion + - card rendering and rerendering + - The same class directly calls `plugin.updateSettings()` from many render-time control handlers. +- Why it matters + - The file is already large enough that small changes can easily affect unrelated behavior. + - UI, cache, mapping, and persistence concerns are mixed. +- Recommended fix + - Split the setting tab into smaller card-focused modules and move mapping/cache orchestration into dedicated presenter/service helpers. +- Estimated implementation size: L +- Risk level: medium +- Suggested test coverage + - Preserve existing UI tests while splitting into smaller component-level tests. + - Add a render-count or localized-rerender test for card-scoped updates. + +### 4.6 Legacy mirrored note-type fields still act as a shadow configuration path + +- Severity: medium +- Area: architecture +- Evidence from real code + - `src/application/config/PluginSettings.ts` stores both `cardTypeConfigs[*].noteType` and legacy fields such as `qaNoteType`, `clozeNoteType`, and `semanticQaNoteType`. + - `normalizePluginSettings()` derives the legacy fields back from `cardTypeConfigs`. + - `src/application/services/RenderConfigService.ts :: resolveNoteModel()` reads the legacy fields instead of `cardTypeConfigs`. +- Why it matters + - There are two representations for the same concept. + - The code currently avoids divergence only by normalization discipline, which is weaker than having one source of truth. +- Recommended fix + - Make `cardTypeConfigs` the only runtime authority. + - Relegate legacy fields to one-way migration only. +- Estimated implementation size: M +- Risk level: medium +- Suggested test coverage + - Migration tests from old settings snapshots. + - Runtime note-model resolution tests that use only `cardTypeConfigs`. + +### 4.7 AnkiConnect failures are not classified into health-check/offline UX + +- Severity: medium +- Area: UX +- Evidence from real code + - `src/infrastructure/anki/AnkiConnectGateway.ts :: invoke()` calls `requestUrl()` without an explicit timeout parameter. + - `src/presentation/AnkiHeadingSyncPlugin.ts :: runSyncCurrentFile()` and `runSyncVault()` catch generic errors and pass them through `renderUnknownUserFacingError()`. + - `src/application/errors/PluginUserError.ts :: toUserFacingMessage()` falls back to raw error text for ordinary `Error` instances. +- Why it matters + - “Anki is not running”, “wrong URL”, “timed out”, and “AnkiConnect returned an action error” are different user problems but converge to a generic failure path. + - This weakens first-run UX and troubleshooting. +- Recommended fix + - Add a dedicated gateway health-check method, explicit timeout handling, and user-facing offline messaging. + - Surface that in settings as a check button or status line. +- Estimated implementation size: M +- Risk level: low +- Suggested test coverage + - Gateway timeout test. + - Plugin-level offline error rendering test. + - Settings health-check UI test. + +### 4.8 Clear-current-file reset deletes remote notes before markdown cleanup succeeds + +- Severity: high +- Area: sync correctness +- Evidence from real code + - `src/application/use-cases/ClearCurrentFileSyncedCardsUseCase.ts :: execute()` deletes note IDs through `ankiGateway.deleteNotes(noteIds)` before calling `markdownMarkerRemovalService.remove(...)`. + - The result can still contain `conflictFiles` and `failureFiles`, meaning markdown cleanup can fail after remote deletion has already happened. +- Why it matters + - A user can end up with deleted Anki notes but stale markers/local traces in Markdown. + - There is no rollback path. +- Recommended fix + - Make reset more recoverable: + - either remove local markers/state first and mark remote deletion as pending, + - or add a two-phase result model with resumable cleanup. +- Estimated implementation size: M +- Risk level: high +- Suggested test coverage + - Conflict during marker removal after remote deletion. + - Restart/resume after partial reset. + +### 4.9 Batch sync is non-transactional across create/update/tag/deck/media phases + +- Severity: high +- Area: sync correctness +- Evidence from real code + - `src/application/services/AnkiBatchExecutor.ts :: execute()` performs deck ensure, media upload, note create, note update, model migration, tag sync, and deck migration in separate phases. + - There is no rollback or checkpoint reconciliation when a later phase fails after earlier remote mutations already succeeded. +- Why it matters + - Partial AnkiConnect failure can leave the remote state ahead of the local marker/state write-back pipeline. + - The plugin already tries to recover marker writes, but not the whole multi-phase sync transaction. +- Recommended fix + - Introduce a resumable execution journal or a narrower phase-by-phase recovery strategy. + - At minimum, classify remote progress in the saved state when later phases fail. +- Estimated implementation size: L +- Risk level: high +- Suggested test coverage + - Partial failure after note creation but before tag/deck sync. + - Partial failure after model migration. + +### 4.10 Test doubles are too optimistic for real Anki multi-request failure behavior + +- Severity: medium +- Area: testing +- Evidence from real code + - `src/test-support/manualSyncFakes.ts :: FakeManualSyncAnkiGateway` batch methods (`addNotes`, `updateNotes`, `changeDecks`, `storeMediaFiles`) always succeed and do not simulate per-item or top-level multi failures. + - The real adapter in `src/infrastructure/anki/AnkiConnectGateway.ts` uses `multi` requests and already contains per-item error parsing logic for some actions. +- Why it matters + - Service-level tests can pass while still underrepresenting real-world failure modes. + - This is one reason the code has many happy-path tests but weaker evidence around partial remote failures. +- Recommended fix + - Add failure-capable fake gateways or integration-style adapter fixtures that can simulate: + - top-level `multi` errors + - partial per-action failures + - timeouts / transport errors +- Estimated implementation size: M +- Risk level: low +- Suggested test coverage + - `AnkiBatchExecutor` partial failure cases. + - `ManualSyncService` recovery after transport failure. + +## 5. Improvement backlog + +### P0: must fix soon + +- Persist `pendingWriteBack` on load and restart. +- Harden destructive reset so remote deletion cannot silently outrun local cleanup. +- Add AnkiConnect timeout/health classification and user-facing offline guidance. +- Add at least one resumable or checkpointed strategy for partial remote sync failure. + +### P1: high value + +- Resolve semantic QA runtime/UI mismatch. +- Unify QA Group model authority and remove dormant managed-model branch or fully adopt it. +- Collapse note-model authority onto `cardTypeConfigs` only. +- Expose rebuild index or remove the dormant entry point. +- Refactor `PluginSettingTab` into smaller modules. +- Improve failure-capable test doubles and add failure-path integration tests. + +### P2: nice to have + +- Improve locale handling so `zh-*` variants do not fall back to English by design. +- Move `query_note.js` and `query_notes.js` into an explicit debug/scripts area or remove them. +- Simplify build/release documentation around root `main.js` vs `dist/plugin/main.js`. +- Add a small settings-page connection test/diagnostic panel. + +### P3: later exploration + +- Dry-run or preview mode before sync. +- Richer conflict explanation UI for marker-write conflicts. +- Lightweight performance telemetry for large-vault scans. + +## 6. Recommended next development plans + +### Module 1: Persist pending write-back across restart + +- Goal + - Make marker conflict recovery survive plugin reloads. +- Why it matters + - The current design already depends on `pendingWriteBack`, but load discards it. +- Files likely to change + - `src/infrastructure/persistence/DataJsonPluginStateRepository.ts` + - `src/infrastructure/persistence/DataJsonPluginStateRepository.test.ts` + - Possibly `src/domain/manual-sync/entities/PluginState.ts` +- Implementation outline + - Distinguish legacy migration from modern-state passthrough. + - Preserve modern `pendingWriteBack` entries. + - Validate marker kinds and required fields on load. +- Tests to add + - Save/load round-trip for modern pending entries. + - Recovery after conflict across repository reload. +- Risk + - Medium +- Suggested prompt title + - `Persist Pending Writeback Recovery` + +### Module 2: Add Anki health check and timeout-aware UX + +- Goal + - Turn Anki offline/slow scenarios into clear, recoverable UX. +- Why it matters + - Current transport errors are too generic for first-run and troubleshooting. +- Files likely to change + - `src/infrastructure/anki/AnkiConnectGateway.ts` + - `src/presentation/AnkiHeadingSyncPlugin.ts` + - `src/presentation/settings/PluginSettingTab.ts` + - `src/presentation/i18n/messages/en.ts` + - `src/presentation/i18n/messages/zh.ts` +- Implementation outline + - Add ping/health-check action. + - Add explicit timeout handling. + - Surface health result in settings and sync command failures. +- Tests to add + - Gateway timeout/offline tests. + - Settings health-check UI tests. + - Plugin command offline-notice tests. +- Risk + - Low +- Suggested prompt title + - `AnkiConnect Health Check UX` + +### Module 3: Bring semantic QA to product parity + +- Goal + - Stop having semantic QA exist only half-way between runtime and settings. +- Why it matters + - Users need explicit control over all active sync-capable card types. +- Files likely to change + - `src/presentation/settings/PluginSettingTab.ts` + - `src/presentation/settings/PluginSettingTab.test.ts` + - `src/application/config/PluginSettings.ts` + - `src/application/services/NoteFieldMappingService.ts` + - `src/application/services/RenderConfigService.ts` +- Implementation outline + - Expose semantic QA in settings with note-type and field mapping. + - Or gate parser/runtime support behind explicit availability. +- Tests to add + - Settings rendering and persistence tests. + - Semantic QA sync path with configured mapping. +- Risk + - Medium +- Suggested prompt title + - `Semantic QA Settings Parity` + +### Module 4: Harden clear-current-file reset + +- Goal + - Make reset resumable and safer under write conflicts or partial failures. +- Why it matters + - Current order can delete remote data before local cleanup finishes. +- Files likely to change + - `src/application/use-cases/ClearCurrentFileSyncedCardsUseCase.ts` + - `src/application/services/MarkdownSyncedMarkerRemovalService.ts` + - `src/infrastructure/persistence/DataJsonPluginStateRepository.ts` + - `src/application/use-cases/ClearCurrentFileSyncedCardsUseCase.test.ts` +- Implementation outline + - Add staged reset state or reorder the operation with recovery markers. + - Preserve enough local state to retry cleanup safely. +- Tests to add + - Conflict after remote delete. + - Retry after restart. +- Risk + - High +- Suggested prompt title + - `Harden Clear Current File Reset` + +### Module 5: Unify QA Group model authority + +- Goal + - Remove the split between user-selected QA Group models and dormant managed-model infrastructure. +- Why it matters + - One codebase should tell one consistent QA Group story. +- Files likely to change + - `src/application/services/QaGroupModelService.ts` + - `src/application/services/QaGroupModelDefinition.ts` + - `src/application/config/ManagedNoteModels.ts` + - `src/application/services/QaGroupSyncService.ts` + - `src/presentation/settings/PluginSettingTab.ts` + - `src/presentation/i18n/messages/en.ts` + - `src/presentation/i18n/messages/zh.ts` +- Implementation outline + - Decide whether the plugin owns a managed QA Group model. + - Remove or fully integrate the losing branch. +- Tests to add + - End-to-end QA Group path for the chosen authority model. + - Copy and settings tests matching the chosen UX. +- Risk + - Medium +- Suggested prompt title + - `Unify QA Group Model Authority` + +### Module 6: Make `cardTypeConfigs` the sole note-model authority + +- Goal + - Complete the settings migration instead of keeping mirrored legacy fields alive. +- Why it matters + - Shadow settings make future bugs more likely. +- Files likely to change + - `src/application/config/PluginSettings.ts` + - `src/application/services/RenderConfigService.ts` + - `src/infrastructure/persistence/DataJsonPluginConfigRepository.ts` + - `src/application/config/PluginSettings.test.ts` +- Implementation outline + - Read note-model names directly from `cardTypeConfigs`. + - Keep legacy values only for load-time migration. +- Tests to add + - Old snapshot migration tests. + - RenderConfigService tests for all card types. +- Risk + - Medium +- Suggested prompt title + - `Retire Legacy Note Type Mirrors` + +### Module 7: Split the settings page by card and service + +- Goal + - Reduce the size and cognitive load of `PluginSettingTab.ts`. +- Why it matters + - The settings page is now one of the main maintenance hotspots. +- Files likely to change + - `src/presentation/settings/PluginSettingTab.ts` + - New helpers/components under `src/presentation/settings/` + - `src/presentation/settings/PluginSettingTab.test.ts` +- Implementation outline + - Extract card-type card, deck card, scope card, and cache/mapping orchestration helpers. + - Keep public UI behavior stable first. +- Tests to add + - Preserve existing behavior with smaller focused tests. +- Risk + - Medium +- Suggested prompt title + - `Refactor Settings Page Cards` + +## 7. Product suggestions + +- Simplify settings mental model + - Make every active sync-capable card type visible and configurable from one consistent card-type surface. + +- Improve onboarding + - Add a first-run AnkiConnect health check and a “what still needs configuration” summary. + +- Add sync preview or dry-run mode + - The existing plan/build architecture is already close to supporting a non-destructive preview step. + +- Make error recovery explicit + - Marker-write conflicts should tell the user what happened, what was already synced remotely, and how to retry safely. + +- Add mapping confidence cues + - Show whether a mapping is from live cache, stale cache, manual override, or incomplete selection. + +- Explain conflicts in UI + - Deck fallback warnings and mapping staleness already exist in code; present them earlier in settings or pre-sync diagnostics. + +- Stop hardcoding old QA Group product language in user copy + - Current strings still refer to `ObsiAnki QA Group 12` even though runtime sync now follows user-selected models. + +## 8. Technical suggestions + +- Tighten service boundaries + - Move settings-page cache and mapping orchestration out of `PluginSettingTab`. + +- Finish config migration + - Keep one note-model authority path and one QA Group model strategy. + +- Strengthen state invariants + - `pendingWriteBack` should be durable, validated, and part of the explicit recovery model. + +- Harden gateway contracts + - Distinguish transport failure, top-level AnkiConnect error, and per-action multi failure. + +- Clean dead or dormant branches + - Remove unmanaged remnants like unused managed QA Group runtime services if they are not the chosen direction. + +- Add lightweight performance instrumentation + - File scan counts, skipped counts, and marker-conflict counts are already available and could be logged or surfaced in debug mode. + +## 9. Testing suggestions + +### Parser + +- Add more pathological QA Group and semantic QA markdown cases: + - mixed fences + - repeated labels + - ambiguous indentation + - empty answer edge cases + +### Mapping + +- Add stale-field and partially renamed-field tests for basic, cloze, semantic QA, and QA Group mappings. +- Add tests for semantic QA settings once surfaced. + +### Sync execution + +- Add partial failure tests after remote note creation but before local write-back. +- Add restart/retry tests using persisted `pendingWriteBack`. + +### Anki gateway + +- Add timeout/offline tests. +- Add explicit `multi` partial failure tests for more actions than model-field loading. + +### Settings UI + +- Add tests for semantic QA controls if kept active. +- Add health-check and stale-cache status tests. + +### Persistence / migration + +- Add save/load tests that preserve `pendingWriteBack`. +- Add versioned migration tests for old settings snapshots after legacy field cleanup. + +### Failure paths + +- Add reset-conflict tests where remote deletion succeeds but local cleanup fails. +- Add deck cleanup partial-failure tests with mixed deletion outcomes. + +### E2E / manual validation + +- Validate against a real Anki instance for: + - Anki offline + - changed note model fields + - QA Group model mismatch + - marker conflict after editing the file between scan and write-back + +## 10. Risks and non-goals + +- Do not rewrite the whole plugin. +- Do not add more settings before fixing the current runtime/UI mismatches. +- Do not keep both user-selected QA Group models and dormant managed QA Group model code long term. +- Do not hide AnkiConnect transport errors under vague generic notices. +- Do not overfit field mapping heuristics to one language or one note template. +- Do not treat generated `main.js` or `dist/plugin/*` as the primary design surface. + +## 11. Quick wins + +- Add a rebuild-index command and list it in the settings command card. +- Treat `zh-CN` / `zh-TW` style locales as Chinese instead of falling back to English. +- Move `query_note.js` and `query_notes.js` under a `scripts/debug/` convention or delete them. +- Update QA Group settings copy to match the actual chosen product model. +- Add a settings-page “Check Anki connection” button. + +## 12. Suggested implementation order + +1. Persist `pendingWriteBack` correctly across save/load/restart. +2. Add AnkiConnect timeout + health-check + offline UX. +3. Harden clear-current-file reset against partial failure. +4. Resolve semantic QA runtime/UI mismatch. +5. Unify QA Group model authority. +6. Retire legacy note-type mirrors and make `cardTypeConfigs` authoritative. +7. Improve failure-capable test doubles and add partial-failure integration coverage. +8. Split the settings page into smaller card-scoped modules. \ No newline at end of file diff --git a/docs/plugin-improvement-next-prompts.md b/docs/plugin-improvement-next-prompts.md new file mode 100644 index 0000000..404d32b --- /dev/null +++ b/docs/plugin-improvement-next-prompts.md @@ -0,0 +1,346 @@ +# Plugin Improvement Next Prompts + +The following prompts are ready-to-use implementation prompts for the top 5 follow-up tasks from the audit. Each prompt is written for a deep implementation run and assumes the agent must inspect real code first. + +## 1. Persist Pending Writeback Recovery + +### Prompt title + +`pending-writeback-persistence` + +### Prompt + +```text +Use the attached request as the primary reference for this task. + +Repository: Anki Heading Sync Obsidian plugin. + +Goal: +Persist pending markdown write-back recovery across plugin reloads/restarts. + +Important instructions: +- Do a real code review first. Do not guess. +- Start from the actual current implementation of pending write-back: + - src/application/services/MarkdownWriteBackService.ts + - src/application/services/FileIndexerService.ts + - src/infrastructure/persistence/DataJsonPluginStateRepository.ts + - src/infrastructure/persistence/DataJsonPluginStateRepository.test.ts +- Verify how pendingWriteBack is produced, consumed, saved, and loaded. +- Minimize source changes. Do not rewrite unrelated persistence code. + +Required workflow: +1. Inspect the real repository code in depth. +2. Write a gap report to: + docs/pending-writeback-persistence-gap-report.md +3. Write a decisions doc to: + docs/pending-writeback-persistence-decisions.md +4. Implement the fix. +5. Add or update tests. +6. Run validation: + - npm run lint + - npm test +7. If validation passes, run build and sync built plugin artifacts to the Obsidian plugin directory. +8. Commit the work with a bilingual commit message. + +Implementation requirements: +- Preserve modern pendingWriteBack entries on load. +- Keep any required legacy migration behavior explicit and isolated. +- Ensure restart/reload does not silently discard recovery entries. +- Do not modify production behavior outside this slice unless required by the fix. + +Validation requirements: +- Add at least one round-trip persistence test for pendingWriteBack. +- Add at least one recovery-oriented test proving the loaded state is usable. + +Build and sync requirements: +- After passing lint/tests, run the project build. +- Sync only built plugin files: + - main.js + - manifest.json + - styles.css if present +- Do not delete or overwrite data.json. + +Commit requirements: +- Commit only the relevant source/test/docs changes for this task. +- Use a bilingual commit message. + +Final response format: +1. gap summary +2. decisions made +3. files changed +4. tests added/updated +5. commands run and results +6. final commit hash +7. final bilingual commit message +8. whether plugin artifacts were synced +9. known risks +``` + +## 2. Add AnkiConnect Health Check and Timeout UX + +### Prompt title + +`anki-health-check-timeout-ux` + +### Prompt + +```text +Use the attached request as the primary reference for this task. + +Repository: Anki Heading Sync Obsidian plugin. + +Goal: +Add explicit AnkiConnect health-check and timeout-aware offline UX. + +Important instructions: +- Review the real repository code before making recommendations or edits. +- Start from these files: + - src/infrastructure/anki/AnkiConnectGateway.ts + - src/presentation/AnkiHeadingSyncPlugin.ts + - src/presentation/settings/PluginSettingTab.ts + - src/application/errors/PluginUserError.ts + - src/presentation/i18n/messages/en.ts + - src/presentation/i18n/messages/zh.ts +- Do not add generic status UI. Make it fit the existing plugin architecture. + +Required workflow: +1. Inspect the real code path for current sync failures and settings UI. +2. Write a gap report to: + docs/anki-health-check-timeout-gap-report.md +3. Write a decisions doc to: + docs/anki-health-check-timeout-decisions.md +4. Implement the feature. +5. Add tests. +6. Run validation: + - npm run lint + - npm test +7. If validation passes, run build and sync built plugin files. +8. Commit with a bilingual commit message. + +Implementation requirements: +- Add an explicit gateway-level health-check path. +- Add timeout-aware handling for slow/offline AnkiConnect behavior. +- Surface a user-facing check in settings and improve sync error messages. +- Keep the UI concise and aligned with the current settings page. + +Testing requirements: +- Add gateway tests for timeout/offline behavior. +- Add plugin/settings tests for the user-facing health-check or error surface. + +Build and sync requirements: +- Sync only main.js, manifest.json, and styles.css if present. +- Never overwrite or delete data.json. + +Final response format: +1. gap summary +2. decisions made +3. files changed +4. UX changes delivered +5. tests added/updated +6. commands run and results +7. final commit hash +8. final bilingual commit message +9. whether plugin artifacts were synced +10. known follow-ups +``` + +## 3. Bring Semantic QA to Settings Parity + +### Prompt title + +`semantic-qa-settings-parity` + +### Prompt + +```text +Use the attached request as the primary reference for this task. + +Repository: Anki Heading Sync Obsidian plugin. + +Goal: +Resolve the mismatch where semantic QA is active in runtime but not surfaced in the settings UI. + +Important instructions: +- Inspect the real repository code first. +- Start from: + - src/application/config/PluginSettings.ts + - src/domain/manual-sync/services/CardIndexingService.ts + - src/application/services/RenderConfigService.ts + - src/application/services/NoteFieldMappingService.ts + - src/presentation/settings/PluginSettingTab.ts + - src/presentation/settings/PluginSettingTab.test.ts +- Decide whether semantic QA should be fully exposed or intentionally gated/disabled, but justify the decision from real code. + +Required workflow: +1. Inspect the actual runtime and settings code. +2. Write a gap report to: + docs/semantic-qa-settings-parity-gap-report.md +3. Write a decisions doc to: + docs/semantic-qa-settings-parity-decisions.md +4. Implement the chosen direction. +5. Add tests. +6. Run validation: + - npm run lint + - npm test +7. If validation passes, run build and sync built plugin files. +8. Commit with a bilingual commit message. + +Implementation requirements: +- If semantic QA remains active runtime behavior, users must be able to configure it coherently. +- If semantic QA is intentionally not ready, runtime behavior must not silently remain active and unconfigurable. +- Preserve existing behavior for basic, cloze, and QA Group paths. + +Testing requirements: +- Add settings UI coverage for semantic QA. +- Add runtime sync coverage for the chosen semantic QA behavior. + +Build and sync requirements: +- Sync only built plugin files. +- Do not overwrite data.json. + +Final response format: +1. gap summary +2. decision taken and why +3. files changed +4. UX impact +5. tests added/updated +6. commands run and results +7. final commit hash +8. final bilingual commit message +9. whether plugin artifacts were synced +10. known tradeoffs +``` + +## 4. Harden Clear Current File Reset + +### Prompt title + +`clear-current-file-reset-hardening` + +### Prompt + +```text +Use the attached request as the primary reference for this task. + +Repository: Anki Heading Sync Obsidian plugin. + +Goal: +Make “clear current file synced cards” safer under marker conflicts and partial failure. + +Important instructions: +- Review the real code before changing anything. +- Start from: + - src/application/use-cases/ClearCurrentFileSyncedCardsUseCase.ts + - src/application/services/MarkdownSyncedMarkerRemovalService.ts + - src/infrastructure/persistence/DataJsonPluginStateRepository.ts + - src/application/use-cases/ClearCurrentFileSyncedCardsUseCase.test.ts +- Keep the change narrowly scoped to reset safety and recoverability. + +Required workflow: +1. Inspect the real destructive path and its current tests. +2. Write a gap report to: + docs/clear-current-file-reset-hardening-gap-report.md +3. Write a decisions doc to: + docs/clear-current-file-reset-hardening-decisions.md +4. Implement the hardening. +5. Add tests. +6. Run validation: + - npm run lint + - npm test +7. If validation passes, run build and sync built plugin files. +8. Commit with a bilingual commit message. + +Implementation requirements: +- Avoid ending in a state where remote notes are deleted but local cleanup cannot be resumed safely. +- If a staged or pending reset state is needed, make it explicit. +- Preserve existing user-facing command behavior unless the safety fix requires improved messaging. + +Testing requirements: +- Add tests for marker conflict or cleanup failure after remote deletion would otherwise occur. +- Add retry/resume coverage if new state is introduced. + +Build and sync requirements: +- Sync only built plugin files. +- Do not overwrite data.json. + +Final response format: +1. gap summary +2. decisions made +3. files changed +4. safety behavior changed +5. tests added/updated +6. commands run and results +7. final commit hash +8. final bilingual commit message +9. whether plugin artifacts were synced +10. residual risks +``` + +## 5. Unify QA Group Model Authority + +### Prompt title + +`qa-group-authority-unification` + +### Prompt + +```text +Use the attached request as the primary reference for this task. + +Repository: Anki Heading Sync Obsidian plugin. + +Goal: +Unify the QA Group note-model strategy so the plugin no longer carries both an active user-selected path and a dormant managed-model branch. + +Important instructions: +- Inspect the real code first. +- Start from: + - src/application/services/QaGroupSyncService.ts + - src/application/services/QaGroupFieldMappingService.ts + - src/application/services/QaGroupModelService.ts + - src/application/services/QaGroupModelDefinition.ts + - src/application/config/ManagedNoteModels.ts + - src/presentation/settings/PluginSettingTab.ts + - src/presentation/i18n/messages/en.ts + - src/presentation/i18n/messages/zh.ts +- Decide one product authority model and remove or fully integrate the other. + +Required workflow: +1. Inspect the real production call sites and tests. +2. Write a gap report to: + docs/qa-group-authority-unification-gap-report.md +3. Write a decisions doc to: + docs/qa-group-authority-unification-decisions.md +4. Implement the chosen authority model. +5. Add tests. +6. Run validation: + - npm run lint + - npm test +7. If validation passes, run build and sync built plugin files. +8. Commit with a bilingual commit message. + +Implementation requirements: +- The final codebase should tell one coherent QA Group story. +- Align settings copy, tests, and runtime behavior with that story. +- Remove or archive dead production code if it is no longer part of the chosen path. + +Testing requirements: +- Add or update tests so the chosen QA Group authority path is fully covered. +- Remove misleading tests that only validate dead code paths. + +Build and sync requirements: +- Sync only built plugin files. +- Do not overwrite data.json. + +Final response format: +1. gap summary +2. authority model chosen and why +3. files changed +4. runtime/UI alignment changes +5. tests added/updated +6. commands run and results +7. final commit hash +8. final bilingual commit message +9. whether plugin artifacts were synced +10. follow-up migration tasks +``` \ No newline at end of file