GeezerVison is showing github.com — Compare purls through one PurlKey type by mikolalysenko · Pull Request #1045 · SocketDev/socket-patch
Conversation
Problem
Architecture audit §3.B (purl equality). At least six relations answered "do these two purls name the same package release", and they disagreed:
purl_eq folded percent-encoding and composer only;
purl_identity_key stripped qualifiers and did composer only;
- two different functions were both named
canonical_base_purl (one in vex::discover and one in rollout);
policy::canon and package_spec_matches each had their own folding;
- the ledger had five ownership matchers with different canonicalization (
covers_purl, purl_keys_cover, lookup_entry, apply's Go --check set, the hosted_pin_of / hosted_vendored_overlap keys);
- there were several local
normalize_purl(strip_purl_qualifiers(..)) closures used as keys.
The canonical_purl doc comment also claimed an identity it doesn't provide.
User-visible effects:
- B20:
scan --prune / --sync dropped a live manifest entry, and GC'd its blobs, when the API spelled the purl pkg:nuget/[email protected] and the NuGet global-cache crawl reported pkg:nuget/[email protected]. Update detection and redirect-candidate matching missed the same entry. docker_e2e_nuget.rs worked around this by serving a lowercase purl from the fixture. A PEP 503 spelling (typing_extensions vs typing-extensions) was pruned the same way.
- B73:
remove / rollback pkg:pypi/[email protected] found nothing when the recorded key was typing-extensions. The same happened for a NuGet case variant.
Related: #553 (the takeover ledger lookup now uses the same key, but there's no e2e for it yet), #748 / C20 (this is the identity half; the builders are untouched), #484 (Go + vs %2B is folded by the key).
Change
- New
utils::purl_key
PurlKey is the release identity. It strips qualifiers and subpath, percent-decodes, lowercases the type, PEP 503-folds PyPI names, case-folds NuGet names and versions, case-folds Composer names, and keys Composer versions by release identity (3.0.2 = v3.0.2 = 3.0.2.0).
PurlKey::qualified keeps release variants apart.
canonical_base_purl moved here. It is the display spelling the key is built from, and VEX product purls still use it.
- Call sites. Every purl identity comparison in core and the CLI now builds
PurlKeys (the exceptions are listed under Deferred):
- ledger ownership;
- prune GC;
- update detection;
- redirect candidates;
- lockfile-only checks (one predicate; the duplicate
notInstalled check now calls it);
- takeover ledger drop;
- hosted pins;
- VEX source matching;
- rollout budget;
- policy reports;
get presence;
- Go-patches orphan prune;
- vlt heal targets;
- Gradle pin rows;
- the hosted memory engine's batch-search owner map,
lock_inventory::lookup, the vendored blob harvest, apply's mismatch-blob record filter, rollback's superseded_by_hosted, and the rollout RecordedIndex (review round, see below).
- Vendored key sets are now
HashSet<PurlKey>, so the compiler routes every construction through the key.
package_spec_matches compares the folded key case-insensitively. User filter specs stay as lenient as before, and a versioned Composer spec still matches by exact release identity.
canonical_purl keeps only its display role. Its doc no longer claims identity.
- Docker e2e:
docker_e2e_nuget.rs now serves the API's real mixed-case spelling for the patch, and scan --sync must keep that entry.
Duplicate copies deleted
Purl equality relations went from 11 to 1 (PurlKey, with canonical_base_purl as its display spelling). Deleted:
utils::purl::purl_eq
composer_version::{purl_identity_key, composer_purl_identity, composer_purls_equivalent, composer_bases_equivalent}
rollout::canonical_base_purl
policy::canon
vex::discover::{canonical_base_purl, same_package}
rollout::stage::qualified_key (a copy of PurlKey::qualified)
lock_inventory::lookup's own PEP 503 / exact-version matcher
vex_sources::same_package
- policy's
canonical_pypi_purl
The ledger matchers (covers_purl, purl_keys_cover, lookup_entry_kv) are now one-line PurlKey comparisons. The ad-hoc key closures in scan/mod.rs, ledgers.rs, list.rs, hosted.rs, get.rs and apply.rs are gone.
Behavior notes
- Policy
filtered[] / retained[] purls and rollout base_purls are the PurlKey spelling, so PyPI and NuGet names there now appear folded. Composer already showed its identity form.
- VEX output is unchanged except for one case: a Composer
dev- branch name keeps its case in canonical_base_purl. Before this PR it was lowercased.
Review round (rebased onto main @ 431b818)
- Rebased onto 14 new main commits (Fix VEX attesting beside an unpatched same-lock copy (#935, #938, #939) #940, Fix gem takeover un-hosting a grouped gem (#775) #776, Fix hosted yarn classic pins missing berry warning (#907) #917, Fix gem crawler missing Bundler .bundle root (#967) #968, Fix pnpm lock/workspace readers missing a BOM (#903, #904, #905) #909, Fix rollback/remove of an agent record superseded by a hosted pin (#933) #934, Honor HTTP-date Retry-After on vendor-service retries through api::retry (#677) #889, then Fix composer e2e flake on packagist connect timeouts #1037, Fix yarn berry project gates drifting between modes (#628, #629) #657, Fix Hatch environments being invisible to stale-install checks and VEX (#335) #700, Fix .bundle/config values keeping a trailing # comment (#951) #953, Scan every vendored-write file for vendored references (#832, #958) #1015, Fix remove/rollback missing PyPI name spellings (#1024) #1025, Fix stale docs and comments, and the npm wrapper's schema and exit code #1022).
- Fix remove/rollback missing PyPI name spellings (#1024) #1025 (fixes
remove and rollback don't PEP 503-normalise PyPI purl identifiers, so remove pkg:pypi/[email protected] exits 1 "No patch found" while get accepts the same identifier #1024, the PyPI half of B73) had brought back utils::purl::purl_eq with a PEP 503 rule. This branch deletes it again: patch_matches already folds PEP 503 through PurlKey. Fix remove/rollback missing PyPI name spellings (#1024) #1025's unit test now checks equality with PurlKey::qualified, and its remove/pypi_name_spellings.rs CLI tests pass unchanged. The get.rs conflict was resolved so Fix VEX attesting beside an unpatched same-lock copy (#935, #938, #939) #940's hosted-claim set is a HashSet<PurlKey> (not canonical_purl strings). The new gem-takeover pin lookup in vendor.rs uses PurlKey::same, and the new rollback::superseded_by_hosted uses PurlKey instead of the deleted helpers.
scan --prune --dry-run now shares the wet vendor GC's manifest-key drop (vendor::unused_vendored_manifest_keys). Before this, the preview used a qualifier-strip relation, so NuGet case, PEP 503 and composer padding variants were pruned for real but missing from the preview.
hosted::memory::discover::batch_search matches response packages to their asking roots by PurlKey. Before this, a respelled answer (such as Newtonsoft.Json) was credited to every root in the chunk.
lock_inventory::lookup matches by PurlKey, so a composer API @3.0.2.0 finds the lock's 3.0.2.
LockfileSupplement.purls is a HashSet<PurlKey> built once. The lockfile-only check is now one hash lookup, not an O(n) re-key on every miss.
hosted_vendored_overlap deduplicates by PurlKey, so one release spelled two ways gives one takeover warning.
- Composer:
PurlKey keeps the sentinel-free composer_version_key so its string stays printable in policy and rollout reports. A new test over the vector file checks that a rejected spelling can never key the same as an accepted one: every accepted key is itself accepted, bare and behind a v. The module docs now say so.
Core API break (for the release notes; CHANGELOG untouched)
The following socket-patch-core public items were removed or moved:
utils::purl::purl_eq
vex::canonical_base_purl (re-export) and rollout::canonical_base_purl; use utils::purl_key::canonical_base_purl
composer_version::{composer_purl_identity, purl_identity_key, composer_purls_equivalent}
rollout::stage::qualified_key
These signatures changed from HashSet<String> to HashSet<PurlKey>:
vendor::vendored_purl_keys
VendorState::purl_keys
vendor::purl_keys_cover
lock_inventory::lookup now also matches composer padding and NuGet/composer name case. Nothing else in the workspace references the removed items.
Testing
Failing first: on main plus only the new tests, all three regression tests failed. On this branch they pass.
cargo test -p socket-patch-core --lib -- folds_pep503_and_nuget_case nuget_case_spellings_are_one_vendored_package: 2 failed.
cargo test -p socket-patch-cli --lib -- detect_prunable_keeps_case_and_pep503: failed. It pruned both Newtonsoft.Json and typing_extensions.
New tests:
Commands run on this branch (macOS):
cargo test -p socket-patch-core -p socket-patch-cli --lib
cargo test -p socket-patch-core --tests --no-fail-fast: all 36 integration suites pass. The lib failure is the same pre-existing one.
cargo test -p socket-patch-cli with these integration tests: scan, remove, rollback, apply, get, vendor, cli, e2e_scan, e2e_nuget, e2e_composer, e2e_composer_version_identity, e2e_socket_yml_policy, scan_rollout_e2e, hosted_memory_{rollout,engine,parity}, covgap_commands_{scan_mod,scan_hosted,rollback,get,vendor,vex}, global_scope_project_state, in_process_{remove_repair_lifecycle,rollback_vendored,vendor}, e2e_vex, e2e_vex_vendor, e2e_vex_redirect, e2e_embedded_vex, coverage_fix_scan_hosted_dryrun_vendored. All passed.
cargo clippy -p socket-patch-core -p socket-patch-cli --all-targets -- -D warnings: no findings in any touched file.
- The local clippy 1.93 reports pre-existing lints in untouched files, such as
jvm_jar.rs, prebuilt_common/mod.rs and a macOS-only unused variable in python_crawler.rs.
- I rustfmt'd only the changed hunks. Files that were already unformatted on
main keep their old formatting.
- After the rebase: focused unit tests for purl, gc, state, policy and rollout pass.
- After the review-round rebase and fixes (macOS):
cargo build -p socket-patch-core -p socket-patch-cli --all-targets
cargo clippy -p socket-patch-core -p socket-patch-cli --all-targets: 0 findings in touched files. The pre-existing lints in untouched files listed above remain.
cargo test -p socket-patch-core --lib: 5641 passed (the digest failure is gone now that Fix main CI red on stale digest pending-list entries #1016 is on main).
cargo test -p socket-patch-cli --lib --test scan --test vendor --test rollback --test apply --test get --test remove --test gradle_agent_cli --test in_process_rollback_vendored --test in_process_vendor: all passed (lib 872, scan 118, vendor 91, rollback 38, apply 106, get 81, remove 94 (incl. Fix remove/rollback missing PyPI name spellings (#1024) #1025's PyPI spelling tests), gradle_agent_cli 34, in_process_rollback_vendored 16, in_process_vendor 121).
- New regression tests:
batch_search_credits_a_respelled_response_only_to_its_asker, lookup_matches_by_purl_identity, overlap_reports_one_entry_per_release_across_spellings, covers_every_spelling_of_the_release (unused_vendored_manifest_keys), and sentinel_free_key_keeps_rejected_spellings_apart. The first three fail against the pre-fix code: a spelling-keyed owner falls back to every chunk root, exact-version lookup misses 3.0.2.0, and spelling dedup gives 4 entries.
- Not run locally:
docker_e2e_nuget (Linux/Docker). It is the end-to-end proof for B20 (mixed-case manifest key against the lowercase global-cache crawl, through scan --sync, GC, apply and agent VEX). It must pass in CI before this lands.
Deferred
- Name-level PyPI comparisons inside lock parsers (
canonicalize_pypi_name(a) == canonicalize_pypi_name(b)) are left alone. They compare names, not purls.
- Same-source qualifier grouping in
vendor.rs / apply.rs (variant_groups, vendored_bases over manifest keys) is left as-is, since both sides already share one spelling.
- Moving
Ecosystem and canonicalize_pypi_name to core (E39 / Move canonicalize_pypi_name and the PEP 508 name scanner out of crawlers and vendor into one PyPI name module #883) is a separate refactor.
- The four target grammars (scan
--package, get fuzzy, remove/rollback, UUID shortcut) are untouched.
- Left as literal spelling comparisons, deliberately:
get.rs's failure dedup on normalize_purl is display-only.
ecosystem_dispatch::merge_qualified matches a crawler's echo of the exact base purl it was asked for, so both sides are the same string.
- The Gradle/Maven scan keys use
canonical_base_purl. Maven coordinates are case-sensitive, so the canonical spelling is the identity, and the lock-file GAVs they are checked against are built as strings.
- The vendored blob harvest matches
PurlKey::new(base_purl), not lookup_entry. It keeps the old qualifier-insensitive match for manifest keys, whereas lookup_entry is qualifier-sensitive.
- No maintainer decision was needed.
🤖 Generated with Claude Code
Note Medium Risk
Wide refactor of identity comparisons across apply, scan, GC, rollback, and policy paths; behavior changes are intentional but any missed call site could still mis-match or over-prune patches.
Overview
Introduces PurlKey as the single release-identity for package URLs (encoding, qualifiers, NuGet/PyPI/Composer spelling variants) and routes prune GC, lockfile-only detection, vendor ownership, rollout, policy, VEX, and hosted batch search through it instead of a dozen overlapping helpers (purl_eq, policy::canon, duplicate canonical_base_purl, composer purl identity helpers, etc.).
User-visible fixes: scan --prune/--sync no longer drops live manifest entries when the API uses mixed-case NuGet or alternate PyPI spellings vs the crawl; remove/rollback identifiers now match those recorded keys. Vendored key sets become HashSet<PurlKey>; dry-run prune preview shares wet vendor GC manifest-key logic via unused_vendored_manifest_keys; hosted memory batch search credits respelled API purls only to the asking root.
Several removed/moved socket-patch-core public APIs (purl_eq, old canonical_base_purl locations, composer identity exports); vendored helpers now take HashSet<PurlKey>.
Reviewed by Cursor Bugbot for commit daa5ef8. Configure here.
Generated by Claude Code
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Oct 7, 2026
Review of #1045 found purl equality checks that still bypassed the key:
- hosted memory batch_search matched response packages to their asking
roots by spelling, so a `Newtonsoft.Json` / `typing_extensions` /
composer `@3.0.2.0` answer fell back to every root in the chunk;
- lock_inventory::lookup had its own matcher (PEP 503 only, exact
version), so a composer API purl `@3.0.2.0` missed the lock's `3.0.2`;
- the vendored blob harvest and apply's mismatch-blob record filter
compared qualifier-stripped strings;
- rollback's superseded_by_hosted (new on main) used the deleted
canonical_base_purl / composer_purls_equivalent pair;
- rollout::stage::qualified_key duplicated PurlKey::qualified (deleted);
RecordedIndex now keys by PurlKey;
- Gradle scan keys use canonical_base_purl (Maven is case-sensitive, so
the canonical spelling is the identity there).
Regression tests: a mixed-case NuGet batch answer is credited only to
its asker; lookup matches composer padding and PEP 503 spellings.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
One type answers "do these two purls name the same package release":
qualifiers and subpath stripped, components percent-decoded, the type
lowercased, PyPI names PEP 503-folded, NuGet names and versions
case-folded, Composer names case-folded and Composer versions keyed by
release identity (3.0.2 = v3.0.2 = 3.0.2.0). PurlKey::qualified keeps
release variants apart. canonical_base_purl (moved here from
vex::discover) stays the display spelling the key is built from.
Property tests check that every spelling variant of a release shares
one key, that no other release does, and that the Composer half equals
release equivalence over the whole shared vector file.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Six "same package" relations disagreed, so a NuGet or PyPI spelling
difference between the API purl, the crawl and the ledger could prune a
live manifest entry, skip a takeover or miss a remove target. Every one
now compares PurlKeys, and the copies are gone:
- deleted utils::purl::purl_eq, composer_version::{purl_identity_key,
composer_purl_identity, composer_purls_equivalent,
composer_bases_equivalent}, rollout::canonical_base_purl,
policy::canon, vex::discover::{canonical_base_purl, same_package},
vex_sources::same_package and policy's canonical_pypi_purl;
- the ledger matchers (covers_purl, purl_keys/purl_keys_cover,
lookup_entry_kv), the hosted/vendored overlap, the hosted pin lookup,
apply --check's Go set, the takeover ledger drop and every local
normalize_purl(strip_purl_qualifiers(..)) closure used as a key now
build PurlKeys; vendored key sets are HashSet<PurlKey>.
Fixes B20: scan --prune no longer GCs a NuGet entry the API spelled
Newtonsoft.Json while the global-cache crawl reports newtonsoft.json
(update detection and redirect candidates use the same key). Fixes B73:
remove/rollback identifiers fold PEP 503 and NuGet case. canonical_purl
keeps only its display role and its doc no longer claims identity.
Policy filter specs stay case-insensitive (they compare the folded key
lowercased), and policy/rollout report purls are the PurlKey spelling,
so PyPI/NuGet names there appear folded.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The wiremock fixture had to serve the crawler's lowercase purl so scan's
GC pass would not prune the manifest entry. Serve the API's real
mixed-case Newtonsoft.Json spelling for the patch instead; scan --sync
must now keep it, and agent VEX names the manifest key's spelling.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Review of #1045 found purl equality checks that still bypassed the key:
- hosted memory batch_search matched response packages to their asking
roots by spelling, so a `Newtonsoft.Json` / `typing_extensions` /
composer `@3.0.2.0` answer fell back to every root in the chunk;
- lock_inventory::lookup had its own matcher (PEP 503 only, exact
version), so a composer API purl `@3.0.2.0` missed the lock's `3.0.2`;
- the vendored blob harvest and apply's mismatch-blob record filter
compared qualifier-stripped strings;
- rollback's superseded_by_hosted (new on main) used the deleted
canonical_base_purl / composer_purls_equivalent pair;
- rollout::stage::qualified_key duplicated PurlKey::qualified (deleted);
RecordedIndex now keys by PurlKey;
- Gradle scan keys use canonical_base_purl (Maven is case-sensitive, so
the canonical spelling is the identity there).
Regression tests: a mixed-case NuGet batch answer is credited only to
its asker; lookup matches composer padding and PEP 503 spellings.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
- `scan --prune --dry-run` previewed the vendor GC's manifest drop with a
qualifier-strip relation while the wet pass used PurlKey, so NuGet case,
PEP 503 and composer padding variants were pruned for real but not in
the preview. Both now call vendor::unused_vendored_manifest_keys.
- LockfileSupplement.purls is a HashSet<PurlKey> built once, so the
lockfile-only predicate is one hash lookup instead of re-keying the
whole set on every miss.
- Ledgers::hosted_vendored_overlap deduplicates by PurlKey, so two
spellings of one release give one takeover warning.
- get's hosted-claim set (rebased onto #940's new code) is a
HashSet<PurlKey>; the new gem takeover pin lookup uses PurlKey::same.
- composer_version: pin that the sentinel-free key PurlKey uses never
lets a rejected spelling collide with an accepted one, and document it.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
cursor
Bot
left a comment
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Lockfile-only skips installed spelling variants
- Changed lockfile_supplement to use PurlKey normalization for exclusion checks, matching the membership check behavior and preventing spelling variants from being incorrectly classified as not installed.

Or push these changes by commenting:
Preview (aae44b9bf5)
diff --git a/crates/socket-patch-cli/src/commands/scan/discovery.rs b/crates/socket-patch-cli/src/commands/scan/discovery.rs
--- a/crates/socket-patch-cli/src/commands/scan/discovery.rs
+++ b/crates/socket-patch-cli/src/commands/scan/discovery.rs
@@ -75,7 +75,7 @@
if entries.is_empty() {
return out;
}
- let crawled_purls: HashSet<&str> = crawled.iter().map(|p| p.purl.as_str()).collect();
+ let crawled_purls: HashSet<PurlKey> = crawled.iter().map(|p| PurlKey::new(&p.purl)).collect();
let in_scope = |purl: &str| {
only.is_none_or(|list| {
socket_patch_core::crawlers::Ecosystem::from_purl(purl)
@@ -83,7 +83,7 @@
})
};
for entry in entries {
- if crawled_purls.contains(entry.purl.as_str()) || !in_scope(&entry.purl) {
+ if crawled_purls.contains(&PurlKey::new(&entry.purl)) || !in_scope(&entry.purl) {
continue;
}
let Some(pkg) = crawled_from_purl(&entry.purl, &common.cwd) else {
You can send follow-ups to the cloud agent here.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
lockfile_supplement excluded installed packages by literal purl but
keyed the remainder by PurlKey, so a lock spelling that differed from
the crawl only in NuGet case, PEP 503 form or composer padding was
recorded as lockfile-only and lockfile_only_contains then flagged the
live install package_not_installed (agent apply skip, vendor baseline
pre-verify, [NOT INSTALLED] marker). Exclude crawled packages by
PurlKey, the same relation the lookup uses, via a testable
lockfile_only_packages helper.
unused_vendored_manifest_keys only keyed the ledger key, but a golang
ledger key can keep the module proxy's !x case encoding while the
manifest holds the decoded spelling, which PurlKey does not bridge. The
earlier wet GC also matched entry.base_purl; restore that through
VendorEntry::covers_purl in both the wet GC and the scan --prune
--dry-run preview so a reverted !burnt!sushi entry no longer leaves its
BurntSushi manifest record and blobs behind.
Co-Authored-By: Claude <[email protected]>
cursor[bot] reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit daa5ef8. Configure here.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
[agent] Ready for review at daa5ef8bfba15b90700b40b8680235601206098f.
- CI: required
ci-ok green. 527 success / 6 skipped / 0 failing; 30 non-required macOS/Windows legs still queued or running. yarn-berry 4.1.0 (ubuntu) failed on attempt 1 with a corepack [email protected] unavailable fetch race (a sibling [email protected] test passed 0.6s later) and passed on re-run.
- Bugbot reviewed
daa5ef8: no unresolved findings. No open review threads.
- Mergeable, no conflicts.
- Reviewer focus:
PurlKey equality and normalization, which now backs every purl comparison.
Generated by Claude Code
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Resolve conflicts against main's rustfmt pass and the containment-helper
refactor (#1042): keep main's formatting, drop the removed `canon` /
composer-identity helpers in favour of PurlKey, and route main's new
apply-failure purl matching in get.rs (#955) through PurlKey::qualified /
PurlKey::same so it agrees with the rest of the purl identity.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Pick up #1093 so PR CI runs without the macOS legs.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Resolve conflicts with #1035 (supersede lifecycle), #1038 (shared repo-root
walk), and #1033 (unwired VEX copies):
- #1035 added `vex::discover::same_release` and a second
`canonical_base_purl` there. `PurlKey::same` already treats composer
version spellings as one release, so every `same_release` call
(vex_sources, rollback, discover, and `ledgers::hosted_pins_matching`)
now uses `PurlKey::same`, and the duplicate helpers are dropped.
- Both sides' new ledgers tests are kept.
- The repo-root lookup in `policy` takes main's `utils::repo_root` version.
`canonical_pypi_purl` stays deleted (nothing calls it now).
- `vex` re-exports main's `UnattestedKind`. `UnwiredCopy::covers` compares
purls with `PurlKey::same` rather than raw string equality.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Resolve the apply `--check` conflict with #1029. Main hoisted the vendored
key set into `run_check`'s outer scope (`vendored_purl_keys`, already a
`PurlKey` set via `purl_keys_cover`). The Go redirect check now reuses that
set instead of loading the vendor ledger a second time.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Upstream moved setup-php's `v2` tag to d52fc211, so the `# v2` comment on
the f3e473d1 pin no longer matches. zizmor's ref-version-mismatch now
fails the org-required "Audit GitHub Actions" check on every PR. f3e473d1
is tag 2.37.2, the label composer-compatibility.yml already uses. This is
the same one-line change as #1118, carried here so this PR can merge;
whichever lands first, the other merges cleanly.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Uh oh!
There was an error while loading. Please reload this page.
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Oct 8, 2026
Resolve the hosted.rs conflict with #1045: this branch moved the
takeover out of hosted.rs into hosted/takeover.rs, so keep that layout
and port #1045's change there. The takeover's ledger drop now compares
PurlKeys and the local canonical_purl key helper is gone.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Oct 8, 2026
Resolve get.rs and scan/vendor_flow.rs against #1045's PurlKey: the
hosted takeover set is now a HashSet<PurlKey> shared by the fetch gate
and the dry-run preview.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Oct 8, 2026
get.rs: keep this branch's split (the download engine lives in
agent_download.rs, short_uuid tests in ui/text.rs) and carry main's
changes to the moved code over to agent_download.rs: store_verified_blob
in write_blob_entry (#726), PurlKey comparisons in lock_text_refusals_for
/ apply_key_covers / fold_apply_failures (#1045), and the base64 engine.
main's new get.rs tests (verified blob writes, linked blob dirs, base64
tolerance, the pnpm-lock FIFO guard) are kept.
list.rs: keep ui::sentence_case (main only reformatted the line).
scan/hosted.rs: add main's yarn_classic_outer closure (#1083).
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Oct 8, 2026
Conflicts resolved:
- vex discover: main's UnattestedKind (#1033) replaces this branch's
parallel UnattestedWhy; the #899 shrinkwrap case becomes
UnattestedKind::NpmShrinkwrapOnly, mapped through vex_sources'
unattested_note like the other kinds.
- scan/policy.rs: the #812 shared-copy keys use PurlKey (#1045) instead
of the removed canon().
- CLI_CONTRACT.md: main's contested/unattested bullets, plus this
branch's #828 withheld-ref sentence, #899 shrinkwrap bullet and npm
warning rows.
- redirect npm.rs / scan mod.rs: imports and key types from both sides.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Mikola Lysenko (mikolalysenko)
added a commit
that referenced
this pull request
Oct 8, 2026
Conflicts:
- ruby_crawler.rs: keep the branch's bundler_config_files (the read-set
paths of the snapshot probe) alongside main's BundlerEnv /
BUNDLE_LOCKFILE; main's new gems.rb stat in
bundler_loaded_manifest_with_env is added to those paths so a reused
discovery re-checks it.
- vex/discover/mod.rs: DiscoverCtx keeps both the branch's view-based
constructor and hosted_hosts memo and main's per-ecosystem read log;
Discovery keeps vlt_bundled_copies plus main's read and withheld.
- testing/golden.rs: ignore all three bookkeeping fields.
canonical_base_purl moved to utils::purl_key on main (#1045); the
branch's three call sites now use it from there.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Mikola Lysenko (mikolalysenko)
pushed a commit
that referenced
this pull request
Oct 8, 2026
Resolve the conflicts with main's move of vendored in-use detection
into discovery (#1050) and its PurlKey rename (#1045):
- npm_flavor.rs: drop the branch's vendored_entry_in_use, now that
Discovery::vendor_entry_in_use replaces it. Bun discovery already
picks the live lock through bun_text_lock_drives, so the #735
regression test now runs through the shared in_use helper with a
minimist entry.
- lock_inventory/tests.rs: keep both sides' new tests.
- vendor.rs: key the Bun reinstall dedupe on PurlKey.
Assisted-by: Claude Code:claude-opus-5-5
Fix open bun issues (#992, #861, #784, #764, #735, #635, #599, #578, #497, #443, #371)
#1009
Merged
Mikola Lysenko (mikolalysenko)
pushed a commit
that referenced
this pull request
Oct 8, 2026
The merge of main (#1045) moved lock_text_refusals_for onto PurlKey, so
canonical_purl is no longer referenced in agent_download.rs and the
workspace clippy job fails on -D unused-imports.
Co-Authored-By: Claude <[email protected]>
This was referenced Oct 9, 2026
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Labels
arch-refactor
PR opened by the scheduled architecture refactor routine
Ready for review
Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review
Milestone
No milestone
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.
|