Problem
Users are misunderstanding the interactive mcs sync picker:
- They don't select packs to install — the picker looks like a read-only list.
- They don't unselect installed packs to remove them — they don't realize the selection is the desired final state, so they assume they need a separate remove action.
The root cause: mcs sync is declarative/convergent (desired state reconciliation), but the UI presents it as a flat selection. The ○/● markers give no cue that pre-checked items represent current state rather than new picks, and there's no preview of the delta before applying.
Proposed solution
1. Delta-aware markers in the picker
Today Sources/mcs/Core/CLIOutput.swift:520 renders two states (● selected / ○ not). Extend to four states that compare the current toggle against the baseline (previously-installed packs, already seeded in Sources/mcs/Sync/Configurator.swift:64):
| Marker |
Meaning |
● (default) |
Installed, staying installed |
●+ (green) |
Will be added |
○- (red) |
Will be removed |
○ (dim) |
Not installed, not selected |
Plus a live footer updating on each toggle: +2 to add, -1 to remove, 3 unchanged.
2. Confirmation screen before apply
After Enter, render a summary and require y to apply:
Review changes (project: /path/to/repo)
+ add: ios, github-planner
- remove: android
= keep: swift
Apply? [y/N]
This catches the misunderstanding even if someone misreads the picker. Skippable when there's nothing to remove (additions-only is the friendly case).
3. Reframe the section header
Rename the picker title from "Tech Packs" to something like "Desired packs (selected = installed after sync)" in Configurator.swift:68-73. One line of copy that reframes the whole interaction.
Tradeoff considered and rejected
Tempting alternative: add mcs install <pack> / mcs remove <pack> as verbs matching user intuition. Rejected because it fragments the convergence engine — two mental models for the same operation, and ProjectState / ProjectIndex ownership tracking assumes a single reconciliation path. Keep sync as the single source of truth and make its intent clearer.
Scope
Touches CLIOutput.swift (picker rendering, new SelectableItem.baselineSelected field) and Configurator.swift (baseline propagation + post-picker confirmation step). Also applies to --global sync since it uses the same picker.
Problem
Users are misunderstanding the interactive
mcs syncpicker:The root cause:
mcs syncis declarative/convergent (desired state reconciliation), but the UI presents it as a flat selection. The ○/● markers give no cue that pre-checked items represent current state rather than new picks, and there's no preview of the delta before applying.Proposed solution
1. Delta-aware markers in the picker
Today
Sources/mcs/Core/CLIOutput.swift:520renders two states (●selected /○not). Extend to four states that compare the current toggle against the baseline (previously-installed packs, already seeded inSources/mcs/Sync/Configurator.swift:64):●(default)●+(green)○-(red)○(dim)Plus a live footer updating on each toggle:
+2 to add, -1 to remove, 3 unchanged.2. Confirmation screen before apply
After Enter, render a summary and require
yto apply:This catches the misunderstanding even if someone misreads the picker. Skippable when there's nothing to remove (additions-only is the friendly case).
3. Reframe the section header
Rename the picker title from
"Tech Packs"to something like"Desired packs (selected = installed after sync)"inConfigurator.swift:68-73. One line of copy that reframes the whole interaction.Tradeoff considered and rejected
Tempting alternative: add
mcs install <pack>/mcs remove <pack>as verbs matching user intuition. Rejected because it fragments the convergence engine — two mental models for the same operation, andProjectState/ProjectIndexownership tracking assumes a single reconciliation path. Keepsyncas the single source of truth and make its intent clearer.Scope
Touches
CLIOutput.swift(picker rendering, newSelectableItem.baselineSelectedfield) andConfigurator.swift(baseline propagation + post-picker confirmation step). Also applies to--globalsync since it uses the same picker.