Context
Four places independently hand-roll HTTP downloads with curl via Open3:
UrlRepository#download_to_tempfile (resolve-time TOFU download+hash)
CmakeIntegration (tarball fetch at install)
GhIntegration (asset download fallback paths)
SteamCmd (steamcmd bootstrap fetch)
Each has its own error type, retry posture (none), progress reporting (none), and tempfile hygiene. That's materialization-layer duplication, not a resolve-seam concern.
Ask
Extract one downloader utility (curl wrapper or Net::HTTP) owning: typed failure (one error base), destination/tempfile management, optional expected-digest verification (the Cache already verifies — decide the seam so verification isn't duplicated either), and consistent shadowenv-free invocation. Migrate the four call sites.
Non-goals
Changing any resolve-time semantics; at() stays pure (no I/O), and UrlRepository#find's download-as-observation semantics stay in find.
Context
Four places independently hand-roll HTTP downloads with
curlviaOpen3:UrlRepository#download_to_tempfile(resolve-time TOFU download+hash)CmakeIntegration(tarball fetch at install)GhIntegration(asset download fallback paths)SteamCmd(steamcmd bootstrap fetch)Each has its own error type, retry posture (none), progress reporting (none), and tempfile hygiene. That's materialization-layer duplication, not a resolve-seam concern.
Ask
Extract one downloader utility (curl wrapper or Net::HTTP) owning: typed failure (one error base), destination/tempfile management, optional expected-digest verification (the
Cachealready verifies — decide the seam so verification isn't duplicated either), and consistent shadowenv-free invocation. Migrate the four call sites.Non-goals
Changing any resolve-time semantics;
at()stays pure (no I/O), andUrlRepository#find's download-as-observation semantics stay infind.