Every plugin listed in plugins.yaml is re-downloaded in full on every start, even when the local file already matches the manifest's sha256.
PluginManagerImpl.downloadPlugins() (:286-329) never reads the existing file. For each entry it fetches the URL, streams into <filename>.tmp while hashing the download, compares that to sha256, and moves the temp over target. target is only ever a move destination — never stat'd, never hashed. So the hash validates the transfer correctly (a mismatch discards the temp and leaves the installed jar untouched), but nothing can short-circuit the fetch.
Hashing target when it exists and skipping the request on a match would close it.
Separately, README:444-445 is inverted relative to this code. It says that when a filename is reused, "that file will be replaced (unless the SHA256 matches)" — but downloadPlugins moves the temp over the target when the hash matches, and declines to replace when it doesn't.
Read from source; not run.
Found with Claude Code.
Every plugin listed in
plugins.yamlis re-downloaded in full on every start, even when the local file already matches the manifest'ssha256.PluginManagerImpl.downloadPlugins()(:286-329) never reads the existing file. For each entry it fetches the URL, streams into<filename>.tmpwhile hashing the download, compares that tosha256, and moves the temp overtarget.targetis only ever a move destination — never stat'd, never hashed. So the hash validates the transfer correctly (a mismatch discards the temp and leaves the installed jar untouched), but nothing can short-circuit the fetch.Hashing
targetwhen it exists and skipping the request on a match would close it.Separately, README:444-445 is inverted relative to this code. It says that when a filename is reused, "that file will be replaced (unless the SHA256 matches)" — but
downloadPluginsmoves the temp over the target when the hash matches, and declines to replace when it doesn't.Read from source; not run.
Found with Claude Code.