Problem
The plugin UI uses a single mode field (idle | syncing | conflict_resolution | delete_confirmation | ...) to control what's shown to the user. The CLI coordinator blocks with Promise.all() until the plugin responds to each decision prompt.
This means every combination of concurrent user decisions (delete during conflict resolution, rename during delete confirmation, etc.) needs a bespoke transition path. The huntercaron/sync-during-conflict branch alone required 5+ commits to navigate mode interactions, and automated reviewers keep flagging plausible-sounding deadlock concerns because the architecture is non-obvious.
Proposed solution: decision queue
Replace the single mode with a FIFO decision queue on the plugin side. The UI renders the front item; sync continues independently in the background.
State:
pendingDecisions: [
{ id: "conflict-batch-1", type: "conflict", data: [...] },
{ id: "delete-Other.tsx", type: "delete", data: [...] },
]
activeSyncState: "idle" | "syncing"
Plugin changes
- Remove mode-based rendering; render based on
pendingDecisions[0]
pending-deletes → push delete decision onto queue
conflicts → push conflict decision onto queue
- User resolves front item → shift off queue, auto-show next or hide
CLI coordinator changes
- Stop blocking on
Promise.all() — fire-and-forget the request
- Plugin responses (
delete-confirmed, conflicts-resolved) become SyncEvents handled by the state machine
- No more deadlock risk because nothing blocks
CLI state machine changes
- Remove
conflict_resolution as a sync mode
watching handles everything; pending user decisions are state, not a mode gate
Why not now
The current patching approach in huntercaron/sync-during-conflict is correct and covers the known edge cases. This refactor is the right long-term fix but touches the reducer, coordinator, and state machine across 4-5 files and needs dedicated test coverage.
Trigger
Open this if another mode-interaction bug surfaces or a new feature requires yet another bespoke mode transition bridge.
Problem
The plugin UI uses a single
modefield (idle | syncing | conflict_resolution | delete_confirmation | ...) to control what's shown to the user. The CLI coordinator blocks withPromise.all()until the plugin responds to each decision prompt.This means every combination of concurrent user decisions (delete during conflict resolution, rename during delete confirmation, etc.) needs a bespoke transition path. The
huntercaron/sync-during-conflictbranch alone required 5+ commits to navigate mode interactions, and automated reviewers keep flagging plausible-sounding deadlock concerns because the architecture is non-obvious.Proposed solution: decision queue
Replace the single
modewith a FIFO decision queue on the plugin side. The UI renders the front item; sync continues independently in the background.Plugin changes
pendingDecisions[0]pending-deletes→ push delete decision onto queueconflicts→ push conflict decision onto queueCLI coordinator changes
Promise.all()— fire-and-forget the requestdelete-confirmed,conflicts-resolved) becomeSyncEvents handled by the state machineCLI state machine changes
conflict_resolutionas a sync modewatchinghandles everything; pending user decisions are state, not a mode gateWhy not now
The current patching approach in
huntercaron/sync-during-conflictis correct and covers the known edge cases. This refactor is the right long-term fix but touches the reducer, coordinator, and state machine across 4-5 files and needs dedicated test coverage.Trigger
Open this if another mode-interaction bug surfaces or a new feature requires yet another bespoke mode transition bridge.