Problem:
saveIssuesToCacheCmd and savePRsToCacheCmd in app.go both fire concurrently at startup via tea.Batch. Each goroutine follows a load → mutate → write-back pattern on the same cache file. Two goroutines doing this simultaneously have a TOCTOU window: both read the same stale cache, then one goroutine's write silently overwrites the other's, causing either issues or PRs to be lost from the cache.
func saveIssuesToCacheCmd(issues []github.Issue) tea.Cmd {
return func() tea.Msg {
c := loadCache() // both goroutines read here
c.SetIssues(issues)
_ = saveCache(c) // one overwrites the other
return nil
}
}
Solution:
Add a sync.Mutex at the package level (or on AppCache) guarding the cache file write. Alternatively, redesign so each save function only writes its own section rather than load-modify-save the whole file.
Acceptance Criteria:
Problem:
saveIssuesToCacheCmdandsavePRsToCacheCmdinapp.goboth fire concurrently at startup viatea.Batch. Each goroutine follows a load → mutate → write-back pattern on the same cache file. Two goroutines doing this simultaneously have a TOCTOU window: both read the same stale cache, then one goroutine's write silently overwrites the other's, causing either issues or PRs to be lost from the cache.Solution:
Add a
sync.Mutexat the package level (or onAppCache) guarding the cache file write. Alternatively, redesign so each save function only writes its own section rather than load-modify-save the whole file.Acceptance Criteria:
saveIssuesToCacheCmdandsavePRsToCacheCmdcannot overwrite each other's datago test -race ./...passes