Repository navigation
Conversation
…he wrong hash A collection member that pins one exact file (a non-fuzzy version with a file hash) was satisfied by any archive in the download folder with the expected file name. The download adapter reuses such a file by name alone, and doDownload then tagged it as the member, so a truncated or different file was installed, failed and was retried against the same file until the user deleted it. Verify an archive before reusing it for such a member: the recorded hash, or when none is recorded the size and then the file's hash (recorded for next time). On a mismatch remove that download and fetch the file again. The tag fallback in findDownloadByReferenceTag no longer resolves the member to an archive whose recorded hash contradicts the rule. Fuzzy members are exempt, and a matching same-named archive is still reused without a download. Fixes LAZ-1286 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… too A recorded hash describes the file as it was when it was hashed, so an archive cut short afterwards still matched. Compare the member's file size with the size on disk first. An archive whose install failed is marked as a failed download, which the reuse check skipped, so a resumed collection stalled on it; verify those as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…chine The first dependency round in the file loads the manager's lazy modules and outlasted waitFor's one-second default under pnpm run verify. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Missing archives and mismatches without a source URL can still be treated as reusable.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Adds validation to prevent collection installs from reusing incorrect same-named archives.
Changes:
- Validates exact references using archive size and MD5.
- Removes mismatched archives and queues replacements.
- Adds regression coverage for reuse and re-download scenarios.
| File | Description |
|---|---|
archiveMatchesReference.ts |
Implements archive validation. |
InstallManager.ts |
Integrates validation into dependency downloads. |
InstallManager.reusedArchive.test.ts |
Tests archive reuse and replacement. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+6574
to
+6576
| if (matches || !hasSource) { | ||
| return dep.download; | ||
| } |
Comment on lines
+60
to
+64
| async function sizeOnDisk(api: IExtensionApi, download: IDownload): Promise<number | undefined> { | ||
| return stat(archivePath(api, download)).then( | ||
| (stats) => stats.size, | ||
| () => download.size, | ||
| ); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Problem
During a collection install, a member that pins one exact file (a non-fuzzy version with a file hash) is satisfied by any archive in the download folder that has the expected file name. Reported on Vortex 2.7.2 and still on master 34f3def. A truncated
Skyland AIO-34179-4-32-1700281282.7z(hash 5428d2c4…; the rule wants a58b879f…) was installed, failed to extract, was requeued and failed again against the same file until the user deleted it. LAZ-1286.findDownloadByRefcorrectly rejects the archive on MD5. But thendownloadURL→IPCDownloadAdapter.#handleStartDownloadreuses it by name (AlreadyDownloaded) without checking size or hash, anddoDownloadadds the rule's referenceTag to it. From then on the rule resolves to that archive by its tag (testRef's tag match, and the tag fallback infindDownloadByReferenceTag, which doesn't check the hash either). After an install failure the download is marked failed, and later resumes stall on it.Change
mod_management/util/archiveMatchesReference.ts(new): decides whether a download can stand in for a reference. Fuzzy references, and references without a hash, always match. Otherwise the file's size on disk must equal the reference'sfileSize, and its hash must match: the recorded hash, or, when none is recorded, the file hashed once on the hash worker and recorded. A file that can't be hashed is reused, as before.InstallManager.ts(+83/−4):downloadURLnow takes the reference it downloads for. When the adapter reuses a file by name,reuseExistingArchivechecks it if it is finished or failed. On a mismatch it removes that download (file and record,remove-downloadwith{confirmed, silent}) and downloads again without the reference, so it cannot loop.downloadDependencyAsyncpasses the requirement on both of itsdownloadURLcalls.downloadMatching(fuzzy members) is unchanged.doDownloadalso checks a member already resolved to a finished, non-bundled archive, for example one tagged by an earlier version.findDownloadByReferenceTagno longer returns a download whose recorded hash contradicts the reference.start-downloadcaller, and only the dependency path knows which file a member pins.Size: 3 files, +578/−4. The test is about 405 of those lines; the non-test change is about 170.
Behaviour changes
statfirst. If it has no recorded hash, it is also hashed once and the hash recorded.Evidence
In the app (A/B): source development builds of base 34f3def and head e520a62 (the later head 861de87 changes only the test's timeouts), Windows 11 on ARM under x64 emulation. Real Nexus collection stardewvalley/rywj0g rev 1 "EasyQoL" (5 required members, all exact), Premium account. Member under test: NPC Map Locations 3.5.2 (fileMD5 8f548631…, 122,700 B), using its real archive truncated to 61,350 B. One run per side per scenario.
no download found, then reused by name and tagged with the rule's tag, install failed "archive appears to be broken", requeued against the same download; next resume downloaded nothing and stalled at 4/5Regression test:
InstallManager.reusedArchive.test.ts, 9 tests. With the non-test change reverted (pr-preflight revert check), 7 fail; the good-copy reuse and the fuzzy exemption pass on both sides, as they should. 9/9 pass on the head.Scoped suites (on fd378f3): mod_management, collections, download_management, IPCDownloadAdapter and util: 1119 passed, 1 skipped, 1 failed — InstallDriver "churn" hit its 5 s timeout under load and passes alone (31/31). Renderer typecheck and lint pass.
pr-preflight (no PR number, Vortex down): 0 fail, 2 warn (size; callers outside the diff, each dispositioned: the requeue/phase/skip scans and both
downloadDependencyAsyncsites are the intended targets; no public signature changed), revert check pass.pnpm run verify: incomplete. The first run, on e520a62, failed three tests under load: this PR's own test (a 1 swaitFor, fixed in 861de87) and two insrc/main(flattenState5 s timeout, manager integration 30 s timeout) that pass when run alone. The re-run on 861de87 was stopped when work was paused.E2E: not started; work was paused before it.
CI: the fork's checks started when this draft was opened; see the Checks tab.
Review
No independent adversarial QA has reviewed this head yet; it was due after
verify.Not covered
downloadMatching's fallback for members with no mod or file id (InstallManager.ts~5591),downloadURL(api, lookupResult, wasCanceled, referenceTag, fileName, undefined, parentCollection)passes the file name ascampaignand drops the real file name. Pre-existing and unrelated; needs its own issue.setDownloadHashByFiledoesn't persistfileMD5: in these runs an adopted file's hash was recorded, so it didn't reproduce.InstallManager.ts; a trial merge of the two branches is clean and the mod_management suites pass on it.🤖 Generated with Claude Code
Written with doodlebot.