Separate change state (repository tree: M/A/D badges) from sync action (queue: ↑↓↺🗑 glyphs) so the Sync Queue shows what will happen, not what already happened.
Scope
Confined to the source-control layer (src/logic/source-control/* + src/ui/source-control/*). The src/logic/** sync domain (SyncManager/SyncPlanner/SyncExecutor/SyncPlanModal) is not touched.
Changes (7 stages)
- SyncAction model — new
src/logic/source-control/SyncAction.ts: SyncAction = 'upload'|'download'|'restore'|'delete-remote', resolveAction(kind), requiresChoice(kind), SyncActionItem.
- SyncActionQueue — new
src/logic/source-control/SyncActionQueue.ts (replaces PushSelectionStore): Map<ChangeId, SyncAction|undefined> with enqueue/dequeue/enqueueMany/dequeueMany/clear/has/getAction/getChangeIds/size/refresh. Action optional on enqueue; ViewModel resolves default at projection.
- Queue presentation —
ChangePresentation.ts drops changeOperation/ChangeOperation; adds ActionView/ACTION_ORDER/presentAction (glyph ↑↓↺🗑). ChangeItem.ts renderQueueItem uses .scv-action-glyph (no .scv-badge).
- View routing —
SourceControlView.ts groups the queue by ACTION_ORDER (group labels only when >1 action); runSync routes by item.action (upload→push, download/restore→pull, delete-remote→remote delete); toggleSelect triggers chooser for requires-choice (local-deleted).
- Local-deleted chooser — new
src/ui/source-control/LocalDeletedActionChooser.ts modal: Restore (CTA) / Delete remote / Cancel; wired in SourceControlItemView.
- Callback wiring —
SourceControlItemView.ts: onDeleteRemote→actionService.deleteRemote, onResolveLocalDeleted→chooser.
- Tests — new
SyncAction.test.ts + SyncActionQueue.test.ts; deleted PushSelectionStore.test.ts; mechanical rename across all affected tests + e2e.
Verification
npx eslint . → 0 errors
npm run build → clean (tsc + Obsidian 1.11.0 compat + esbuild)
npx vitest run → 64 files / 712 tests pass
./init.sh → green
- Manual Obsidian verification (desktop + mobile): pending
Out of scope (deferred)
SyncPlanModal (driven by SyncPlan/PushCoordinator/PullCoordinator confirmPlan) still shows state-based plan rows during coordinator execution — fully refactoring it to action-based review would cross the src/logic/** sync-domain boundary.
Separate change state (repository tree: M/A/D badges) from sync action (queue: ↑↓↺🗑 glyphs) so the Sync Queue shows what will happen, not what already happened.
Scope
Confined to the source-control layer (
src/logic/source-control/*+src/ui/source-control/*). Thesrc/logic/**sync domain (SyncManager/SyncPlanner/SyncExecutor/SyncPlanModal) is not touched.Changes (7 stages)
src/logic/source-control/SyncAction.ts:SyncAction = 'upload'|'download'|'restore'|'delete-remote',resolveAction(kind),requiresChoice(kind),SyncActionItem.src/logic/source-control/SyncActionQueue.ts(replacesPushSelectionStore):Map<ChangeId, SyncAction|undefined>withenqueue/dequeue/enqueueMany/dequeueMany/clear/has/getAction/getChangeIds/size/refresh. Action optional on enqueue; ViewModel resolves default at projection.ChangePresentation.tsdropschangeOperation/ChangeOperation; addsActionView/ACTION_ORDER/presentAction(glyph ↑↓↺🗑).ChangeItem.tsrenderQueueItemuses.scv-action-glyph(no.scv-badge).SourceControlView.tsgroups the queue byACTION_ORDER(group labels only when >1 action);runSyncroutes byitem.action(upload→push, download/restore→pull, delete-remote→remote delete);toggleSelecttriggers chooser for requires-choice (local-deleted).src/ui/source-control/LocalDeletedActionChooser.tsmodal: Restore (CTA) / Delete remote / Cancel; wired inSourceControlItemView.SourceControlItemView.ts:onDeleteRemote→actionService.deleteRemote,onResolveLocalDeleted→chooser.SyncAction.test.ts+SyncActionQueue.test.ts; deletedPushSelectionStore.test.ts; mechanical rename across all affected tests + e2e.Verification
npx eslint .→ 0 errorsnpm run build→ clean (tsc + Obsidian 1.11.0 compat + esbuild)npx vitest run→ 64 files / 712 tests pass./init.sh→ greenOut of scope (deferred)
SyncPlanModal(driven bySyncPlan/PushCoordinator/PullCoordinatorconfirmPlan) still shows state-based plan rows during coordinator execution — fully refactoring it to action-based review would cross thesrc/logic/**sync-domain boundary.