Problem
Two writers own @Shared(.repositoryAppearances) with no sync path between them:
RepositorySettingsFeature.persistAppearance (RepositorySettingsFeature.swift:450-464) writes the whole state.appearance snapshot, captured once when the pane is built (AppFeature.swift:399-406).
- Automatic icon detection commits
.detectedImage icons into the same dict via read-modify-write (RepositoriesFeature+IconDetection.swift:79-84).
appearanceLoaded is never sent in production (tests only), so the Settings pane never refreshes its snapshot.
Failure scenario
Icon detection lands while Settings → repository pane is open; the user then clicks a color swatch. The stale {color: X, icon: nil} snapshot overwrites the detected icon — it vanishes from sidebar/shelf/canvas and from disk, and the imported asset file is leaked.
Suggested fix
Make persistAppearance mutate fields inside withLock ($0[id]?.color = …) instead of replacing the whole snapshot; alternatively give the pane a refresh path when the shared dict changes.
Related: #623 (same whole-snapshot writeback pattern), #622 (the persist also lives in a cancellable effect).
Origin
TCA-conventions audit of the Settings area (2026-07-30), agent finding — not yet re-verified line by line; confirm the exact write sites before fixing.
Problem
Two writers own
@Shared(.repositoryAppearances)with no sync path between them:RepositorySettingsFeature.persistAppearance(RepositorySettingsFeature.swift:450-464) writes the wholestate.appearancesnapshot, captured once when the pane is built (AppFeature.swift:399-406)..detectedImageicons into the same dict via read-modify-write (RepositoriesFeature+IconDetection.swift:79-84).appearanceLoadedis never sent in production (tests only), so the Settings pane never refreshes its snapshot.Failure scenario
Icon detection lands while Settings → repository pane is open; the user then clicks a color swatch. The stale
{color: X, icon: nil}snapshot overwrites the detected icon — it vanishes from sidebar/shelf/canvas and from disk, and the imported asset file is leaked.Suggested fix
Make
persistAppearancemutate fields insidewithLock($0[id]?.color = …) instead of replacing the whole snapshot; alternatively give the pane a refresh path when the shared dict changes.Related: #623 (same whole-snapshot writeback pattern), #622 (the persist also lives in a cancellable effect).
Origin
TCA-conventions audit of the Settings area (2026-07-30), agent finding — not yet re-verified line by line; confirm the exact write sites before fixing.