Problem
ConfiguratorSupport.scanForUndeclaredPlaceholders (Sources/mcs/Sync/ConfiguratorSupport.swift:358-412) walks pack.components with no awareness of --customize exclusions:
for pack in packs {
for component in pack.components {
switch component.installAction {
case let .copyPackFile(source, _, _): ...
case let .settingsMerge(source): ...
case let .mcpServer(config): ...
So a placeholder that appears only inside a deselected component's files is still collected, and Configurator.swift:692-706 prompts for it:
…for a file that will never be installed. The answer is stored in state.resolvedValues and substituted nowhere.
Configurator.configure already has excludedComponents in hand (Configurator.swift:228) and threads it into auto-install, template preloading, artifact installation, and settings composition. resolveAllValues (:642) is the one step that never received it.
Fix sketch
Thread excludedComponents from configure() → resolveAllValues → scanForUndeclaredPlaceholders, and filter inside the component loop:
for pack in packs {
let excluded = excludedComponents[pack.identifier] ?? []
for component in pack.components where !excluded.contains(component.id) {
Templates (includeTemplates: true, global scope) stay unfiltered — template dependency filtering is a separate mechanism handled by preloadTemplates.
Care required
This scan is load-bearing: it is the safety net that catches placeholders which would otherwise survive emitWarnings: false substitution and get written literally into installed files. Narrowing it must not create a hole for a selected component:
- Filter strictly by component id — do not narrow by artifact family or source path.
- Excluded-but-required components (
ComponentDefinition.isRequired) can never appear in the exclusion set (ConfiguratorSupport.selectComponentExclusions:140-141), so they stay covered.
Tests
Tests/MCSTests/LifecycleIntegrationTests.swift (or ConfiguratorSupport-level unit tests):
- placeholder only in an excluded component's
copyPackFile source → not prompted
- same placeholder also used by a selected component → still prompted (regression guard)
- excluded
settingsMerge source and excluded mcpServer env/command/args → not prompted (all three families the scan covers)
- no exclusions → unchanged behavior
Relation to #356
#356 proposes requiredBy on prompts, and correctly identifies that narrowing this scan is a prerequisite — otherwise a requiredBy-skipped prompt reappears as a raw Set value for X fallback. This half is independently valuable and carries no manifest schema change, so it is being split out.
Problem
ConfiguratorSupport.scanForUndeclaredPlaceholders(Sources/mcs/Sync/ConfiguratorSupport.swift:358-412) walkspack.componentswith no awareness of--customizeexclusions:So a placeholder that appears only inside a deselected component's files is still collected, and
Configurator.swift:692-706prompts for it:…for a file that will never be installed. The answer is stored in
state.resolvedValuesand substituted nowhere.Configurator.configurealready hasexcludedComponentsin hand (Configurator.swift:228) and threads it into auto-install, template preloading, artifact installation, and settings composition.resolveAllValues(:642) is the one step that never received it.Fix sketch
Thread
excludedComponentsfromconfigure()→resolveAllValues→scanForUndeclaredPlaceholders, and filter inside the component loop:Templates (
includeTemplates: true, global scope) stay unfiltered — template dependency filtering is a separate mechanism handled bypreloadTemplates.Care required
This scan is load-bearing: it is the safety net that catches placeholders which would otherwise survive
emitWarnings: falsesubstitution and get written literally into installed files. Narrowing it must not create a hole for a selected component:ComponentDefinition.isRequired) can never appear in the exclusion set (ConfiguratorSupport.selectComponentExclusions:140-141), so they stay covered.Tests
Tests/MCSTests/LifecycleIntegrationTests.swift(orConfiguratorSupport-level unit tests):copyPackFilesource → not promptedsettingsMergesource and excludedmcpServerenv/command/args → not prompted (all three families the scan covers)Relation to #356
#356 proposes
requiredByon prompts, and correctly identifies that narrowing this scan is a prerequisite — otherwise arequiredBy-skipped prompt reappears as a rawSet value for Xfallback. This half is independently valuable and carries no manifest schema change, so it is being split out.