Repository navigation
socket-patch architecture (living document): review, defect register and refactoring log #560
Replies: 90 comments
Architecture defect register[agent] This comment is the living register of architectural problems in the socket-patch CLI. It starts from the October 2026 review, which the top post and Parts 2–9 below keep as a living document, and grows with what the scheduled audit routines find. The status column here drives every status shown in the living document. It is regenerated from the Progress: 148 problems tracked · 18 fixed · 5 partly fixed · 1 already fixed · 31 in PR · 72 filed · 6 decision pending · 11 to verify · 4 rejected. On GitHub: 104 open and 26 closed
Ecosystems and formats (
|
| ID | P | Problem | Source | Issues | Status |
|---|---|---|---|---|---|
| E01 | 1 | Hosted NuGet source mapping regex-scanned raw XML, so a commented-out <add key> changed the mapping. |
§1 #5; 5.4 | #561 | fixed (#597) |
| E02 | 1 | bun.lockb vendoring hard-coded registry.npmjs.org; the npm tarball URL and NPM_REGISTRY were spelled twice. |
§1 #7; 4.4 | #562 | fixed (#574); partly not a defect |
| E03 | 1 | vlt registry_base had two implementations with different fallback orders. |
§1 #7; 4.4 | #562 | fixed (#574) |
| E04 | 1 | Hosted-PyPI-URL recognition: Pipenv accepted any host and owned_url rejected path-prefixed origins and sdists (merged E49). Three recognizers remain: hosted_patch_uuid, hosted_pypi_reference/hosted_artifact_url, Pipenv owned_url. |
5.4; new finding | #563 | partly fixed (#572); three recognizers remain (re-checked 431b818) |
| E05 | 1 | Cache crawls aren't project-scoped: cargo, go, maven, nuget and deno enumerate the whole machine cache, and scan sends all of it to the API (#265). | 6.6 | #595 | filed #595; tracking, children #427, #265 |
| E06 | 1 | NuGet assets and cargo vendor/<crate>/Cargo.toml crawler reads weren't FIFO-safe. |
6.6 | #592 | fixed (#602) |
| E07 | 2 | package-lock has four entry walks (inventory, vendored, hosted, restore) with copied identity/skip rules and two pointer escapes. Target: one addressed walk. The serializer half was fixed by #357 (all writers use JsonLayout). |
4.4; 4.7 E | #663 | in PR #1008 |
| E08 | 2 | yarn has seven split("\n\n") + regex grammars beside scan_blocks (redirect/mod.rs ×5, upstream/npm.rs ×2); the grammar is decided three ways (B60) and the regex replacement is unescaped. Target: writers and restorers on formats/yarn. |
3.7 #3; 4.7 D | in PR #1057; confirmed at 1c6c509 (7 splits) |
|
| E09 | 2 | Yarn berry gates were written twice (cacheKey, mixed-EOL and compressionLevel refusals with different codes); hosted didn't gate a mixed-EOL root package.json (merged E51). |
4.4; new finding | #629, #628 | fixed (#657) |
| E10 | 2 | XML has eight hand-rolled scanners and four attribute extractors; the writers (nuget_feed.rs, maven_repo.rs) never use the shared readers. Merged: NuGet config's three readers (was E11) and Maven's two declares_modules (was E55). |
5.4; 3.7 #3 | #715, #716, #717, #594 | filed #715, #716, #717, #594; NuGet hosted reader fixed (#597) |
| E12 | 2 | pnpm v9 and legacy 5.4/6.0 were near-copies; v9 still has two lookup paths (a linear scan and LockIndex, pnpm_lock.rs:1524-1538). |
4.4; 4.5 #4; 4.7 B/G | partly fixed (#583); copies merged, v9 dual lookup remains (pnpm workstream, #1007) | |
| E13 | 2 | utils/poetry_lock.rs ≈ utils/pdm_lock.rs (identical *_lock_edits). |
5.4 | #694 | fixed (#703) |
| E14 | 2 | Pipfile.lock is written two ways: vendored mode re-serializes it, while hosted mode splices spans. | 5.4 | to verify; present on 045d7ec, no drift proven (pipenv writes canonical JSON), not filed yet | |
| E15 | 2 | Cargo.toml [package] is read five ways and has drifted on BOM, [project] and dotted keys; hosted plan_cargo_toml is a line scanner that refuses a multi-line features array (merged E57). |
5.4; 3.7 #3 | #693, #757 | filed #693, #757 |
| E16 | 2 | CRLF has five policies in the npm family and three for toml_edit output; common::detect_eol contradicts LineEndings::Mixed; the "any \r\n → CRLF" rule is written 14 times and gradle::newline_of uses a first-line rule (merged E60). Target: one line-ending policy. |
4.4; new finding | #814, #815 | filed #814, #815; tracking #814, child 1 #815 |
| E17 | 2 | "Is a bun lock present" is asked at seven sites with three semantics (lstat, exists, is_file). With a dangling bun.lock symlink, Bun and the writers use bun.lockb, but the inventory returns nothing. |
4.4 | #735 | in PR #1009 |
| E18 | 3 | JS helper copies: JSON-pointer escape ×2, wiring lines↔JSON ×3, name@spec split ×2, KIND_* re-spelled, uneven recursion bounds, regexes compiled per dependency. |
4.4 | #835 | filed #835; wiring kinds + lines↔JSON codec |
| E19 | 3 | Gem has three section models and three DEPENDENCIES-name parsers (vendored sees only the first GEM section, #779). Go's module readers were duplicated (fixed by #870). |
5.4 | #780, #781 | filed #780; gem models open, go.mod module reader fixed (#870) |
| E20 | 3 | Pure codecs (bun_lockb.rs, bun_lock_text.rs, vlt_lock_text.rs) and the neutral types live outside formats/, creating formats↔vendor/redirect/vex cycles. |
2.1; 4.5 #2; 4.7 J | #833, #834 | filed #833, #834; tracking #833, child 1 #834 |
| E21 | 2 | Tracking: VendorBackend trait + registry. The ecosystem list is enumerated at 16 production sites, and the vend! / vend_installed! macros stand in for the trait. |
2.1; 5.2; 5.8 | #959, #960 | filed #959, #960; tracking #959 (enum dispatch first), child 1 #960 (revert/in-use) |
| E22 | 2 | The JS vendor driver skeleton is copied seven times (guard_coordinates → … → a literal VendorEntry; pnpm legacy shares v9's since #583). Target: one generic driver + NpmLockBackend. |
4.4; 4.7 C | #920, #922 | in PR #1008; tracking #920 and child #922 both claimed (draft) |
| E23 | 2 | The pypi_{poetry,pdm,pipenv}.rs backends repeat one skeleton: load_*_project, classify_dependency, check_target_guards, wire_*, revert_*. |
5.4 | #937 | filed #937; shared wire/revert envelope + one LockTarget (no drift proven) |
| E24 | 2 | There are nine revert mechanisms (~3.5K lines). Target: one splice-record revert engine, with legacy ledger kinds adapted at load. | 2.1; 5.3; 5.8 | #989, #990 | filed #989, #990; tracking #989, child 1 #990 (finish step ×12, four keep policies) |
| E25 | 3 | Per-backend copies: cleanup_failed_stage and <eco>_service_copy (cargo, composer, gem, golang). |
5.4; 5.8 | #906 | filed #906 |
| E26 | 3 | JVM has two Maven backends. Target: merge maven_repo.rs into jvm/ as Shape::Single; its three artifact roots don't follow <eco>/<uuid>. |
5.7 | #971, #972, #973 | in PR #1036; decided #973: one Maven backend, single poms as a reactor of one, legacy roots refused until vendor --revert |
| E27 | 3 | The per-package call model needs ~3K lines of compensating machinery (group_commit, durability, prestage, vendor_prefetch, 22 ParseMemo statics, ledger_snapshots). Target: batched pure planners, after E21 and E24. |
2.4; 5.7 | to verify | |
| E28 | 2 | Dead vendored scaffolding: one-variant VendorSource/PackageSource, an unreachable SERVICE_ECOSYSTEMS refusal, ServicePolicy::new ignoring its config, an unused vend_installed!. |
5.6; R11 | #800, #746 | filed #800, #746 |
| E29 | 3 | registry_fetch.rs (1.5K lines) is really archive extraction, integrity checks and the hosted-restore HTTP client, so it is misnamed and in the wrong place. |
5.6 | #1012 | filed #1012; archive + Go module zip move; integrity (after #834) and registry bases (after #876) remain |
| E30 | 2 | Split redirect/mod.rs (21.9K lines at db83f01: 7.6K prod, 14.3K inline tests) mechanically: model, driver, one file per ecosystem, hosted_url, sibling test files. |
3.7 #1 | #1010, #1011 | filed #1010, #1011; tracking #1010, child 1 #1011 (inline tests out) |
| E31 | 2 | Tracking: trait HostedRewriter + Outcome { per_dep }. It replaces 27 RewriteResult uuid sets, merge_group_delta and the 25-outcome confirm(). |
2.1; 3.7 #2 | #1075, #1076 | filed #1075, #1076; tracking #1075, child 1 #1076 (one report map, mechanical) |
| E32 | 2 | There are two hosted orchestrators, disk (run_redirect_selected) and in-memory (hosted/memory), kept equal by parity tests. Target: one pipeline. Depends on E44. |
3.3; 3.7 #4 | to verify | |
| E33 | 3 | Upstream restore rebuilds originals from the network (~8.8K production lines at 1c6c509, ignores mirrors, re-pins integrity from the registry: B72). Target: an originals sidecar or a narrowed restore. Depends on E45. |
2.3; 3.5 | to verify | |
| E34 | 3 | Per-PM auto-config costs more than it's worth: npm allow-remote (~900 lines re-implementing npm config), pnpm trustLockfile, the vlt warm-tree heal, and the unbenchmarked parallel rewriter groups. |
3.6; 3.7 #7 | #782 | to verify; dead vlt ledger helpers in #782 (E58) |
| E35 | 3 | Retire the refactor oracles: the redirect equivalence suites and 252 KB of goldens, the crawler oracles (3K lines), and the telescoping entry points (use one RewriteOptions). |
3.7 #9; 6.6 | to verify | |
| E36 | 2 | Tracking: one Inventory. Today's four discovery systems are merged by fabricating CrawledPackages with fake node_modules/<name> paths. Rename vex::discover (it is the hosted-state store). |
2.1; 6.2; 6.7 | to verify | |
| E37 | 2 | is_safe_{cargo,gem,nuget}_coordinate are byte-identical and duplicate simple_purl's check. composer_crawler::normalize_version duplicates strip_leading_v. |
6.4 | #630 | filed #630 |
| E38 | 2 | The product-manifest probe table is copied three times and has drifted: vex.rs lacks the csproj and gemspec probes. The probes don't reuse the format parsers. |
new finding; 6.5 | #816 | filed #816; CLI probe-table copy (product_undetected message); parser reuse is #693/#781/#715; Gradle/sbt probes are E87 |
| E39 | 3 | canonicalize_pypi_name lives in crawlers/ (29 importers) and Ecosystem in crawlers/types.rs, while LockfileEntry.ecosystem is a string. Target: core/src/ecosystem.rs. |
2.1; 6.4 | #883 | filed #883; Ecosystem move overlaps #773, #748 |
| E40 | 2 | vex_consumed.rs, in the CLI, is a third copy of package-manager layout knowledge; npm alias discovery is written twice (core alias_copies, vex_consumed) and drifts on case (merged E62). Target: per-ecosystem locators in core. |
6.5; new finding | #855, #856 | filed #855, #856; tracking #855; child #856 in PR #1008 (draft) |
| E41 | 2 | Dead discovery code: lock_inventory/wired.rs has no production caller. |
6.4; 6.5; R11 | #801 | filed #801; empty deno.rs extractor is deliberate, pre-v5 ledger readers are live migration |
| E42 | 2 | The embedded --vex glue is copied per command (scan, apply, and vendor ×3), each with caller-injected bypass sets. Target: one EmbeddedVex helper. |
6.5; R13 | #966 | decision #966; whether --vex stays embedded (Q2) is undecided |
| E43 | 2 | Fail closed on unmodeled resolution config in one shared place. | 2.2 #3 | #458 | partly fixed; gradle.lockfile, BUNDLE_GEMFILE, virtualStoreDir and mirrors are modelled, npm linked-store alias fixed (#987); go.work remains (#458, E81) |
| E44 | 2 | Decide: the napi addon and in-memory engine. Will depscan adopt it (then delete the TS rewriters), or should it be deleted (−4.8K prod)? | §6 Q1; 3.7 #6 | decision pending; no decision issue filed yet | |
| E45 | 2 | Decide: hosted rollback. Is an originals sidecar acceptable, or should restore be narrowed to formats whose original is a pure function of registry data? | §6 Q5; 2.3 | decision pending; no decision issue filed yet | |
| E46 | 2 | Decide: VEX evidence. Should not_affected require consumed evidence by default, so that wired-only evidence (lockfile_basis_ok) needs an opt-in? About 46 open false-attestation issues (B05). |
§6 Q4; 2.2; 6.5 | decision pending; no decision issue filed yet | |
| E47 | 3 | Decide: support tiers for bun.lockb writes, vendored pnpm 7/8, vlt pre-1.0 encodings and hosted pnpm ≤ 6, and whether hosted JVM ships as beta. |
§5; §6 Q3; 4.6 | decision pending; no decision issue filed yet | |
| E48 | 3 | Discovery re-implements package-manager layouts (venv-name hashing, global prefixes, the pnpm store). Target: ask the package manager (poetry env info -p, pipenv --venv, npm query, …). |
2.2 #2; 6.6 | to verify | |
| E50 | 2 | Hosted rewrite_nuget and upstream restore rewrite packages.lock.json entries of the patched id at other versions (every framework); vendored locked_at filters by version. Four lock walkers. |
new finding | #593 | filed #593 |
| E52 | 3 | vendor/go_sum_edit.rs: free go.sum helpers have no production caller and are re-implemented by GoSumEditor; the pure hosted codec lives in vendor/. |
new finding | #631 | filed #631 |
| E53 | 2 | Vendored pnpm wrote the root package.json with serialize_json (lost CRLF, refused BOM). |
new finding | #662 | fixed (#810) |
| E54 | 3 | Poetry and PDM lock rewriters restored line endings with two rules. | new finding | #695 | fixed (#703) |
| E56 | 2 | Lock inventory read only Gemfile.lock, so a gems.rb project's gems.locked was invisible. |
new finding | #736 | fixed (#750) |
| E58 | 3 | Production pub fns with no production caller, orphaned by #277 (committed_artifact_intact, go_sum_edit::remove_lines, hosted-vlt ledger helpers, three test-only helpers). |
new finding; 3.6 | #782 | filed #782 |
| E59 | 2 | Vendored gem edit_lock searched only the first GEM section. |
new finding | #779 | fixed (#805) |
| E61 | 2 | The vendored-reference scan never saw NuGet, Maven or hatch.toml wiring, so the orphan sweep could delete a still-wired feed, repository or wheel (merged E67). |
new finding | #832, #958 | fixed (#1015) |
| E63 | 2 | Hosted Maven splices the API maven_suffixed_version into pom.xml unchecked, while hosted Gradle refuses a malformed suffix (redirect_gradle_override_invalid); four suffix builders, no shared validator (executed twice). |
new finding | #882 | filed #882 |
| E64 | 2 | No shared BOM helper (4 strip_bom copies, ~50 inline strips); formats::pnpm's lockfileVersion readers skipped it (executed twice). Symptoms #903, #904, #623. |
new finding; 4.4 | #905 | partly fixed (#909); pnpm readers skip the BOM, the shared helper remains (#905) |
| E65 | 3 | Every vendored sink discards force/sources, yet vendor --force help and CLI_CONTRACT.md promise missing-file tolerance and a warning nothing emits (executed twice). Bears on #615. |
new finding; 5.2 | #923 | filed #923 |
| E66 | 2 | Vendored Poetry picks its forward splicer by line ending (CRLF/legacy: utils::poetry_lock; LF 2.x: toml_surgery scanner); different files shapes, and only the engine checks wheel name and digest case (executed twice). |
new finding; 5.4 | #936 | filed #936 |
| E68 | 2 | Vendored gem: forward treats any Gemfile containing the copy path as wired, revert needs the exact recorded line, and records revert non-atomically: a trailing comment leaves the lock restored and the Gemfile on path:, drift-keeping forever (executed 3×). Same class as #977. |
new finding; 5.3 | #988 | filed #988 |
| E69 | 2 | "Is this an sbt/Mill/scala-cli build" is six marker lists (#690); a scala-cli dir with only .scala-build or an sbt root with only project/build.properties gets no Coursier roots (executed twice). |
new finding | #1014 | in PR #1032; defects A and B, the .mill-version rule remains |
| E70 | 1 | The vendored→hosted takeover reverts first and plans later: a refused rewrite leaves the package patched in neither mode; the disk write isn't transactional (B14); the memory list drifts (B15); --dry-run counts every takeover (B37). |
audit B03, B14, B15, B37 | #945, #723 | in PR #1039; dry-run residuals #668, #744, #891, #979 open |
| E71 | 1 | The supersede/re-pin lifecycle has no owned-pin generation model: an older uuid's vendored entry, Cargo registry block, Go go.sum pair or Maven -socket.<hex8> repository survives a re-pin or remove. |
audit B07 | #999, #864, #682, #266 | in PR #1035; #954 in #1008, #742/#650 in #943 |
| E72 | 1 | VEX attests not_affected from wiring the package manager doesn't consume: yarn Plug'n'Play loaders, pnpm bundled copies and deno.lock npm copies. |
audit B04, B05 | #519, #406 | in PR #1033; Maven integrity_required = false waits on E46 |
| E73 | 2 | "Is this hosted patch pinned" is decided four ways (confirm, mark_pinned, memory_recorded, discovery); lockless NuGet/Cargo pins are attested in-run, then reported contested forever with a looping remedy. |
audit B13, B58 | #567, #260 | in PR #1058; lockless pins wait on E45 |
| E74 | 2 | "Is this vendored entry still in use" has four answers; six ecosystems' entries are never pruned and scan resurrects them; JVM orphans are invisible unless the ledger is empty; legacy Maven/NuGet "wired" is a substring check. | audit B19, B61, B62 | in PR #1050 | |
| E75 | 2 | Which lockfile governs installs is decided in ≥8 places; vendored and hosted patch different PyPI locks and a takeover restores the losers unpatched. | audit B31 | #612 | in PR #1044; vlt and Pipenv precedence policy needs a maintainer call |
| E76 | 2 | Hosted yarn classic replaces file:, URL and codeload copies with the Socket artifact and rollback corrupts the key; there is no shared copy-source classifier. |
audit B16 | in PR #1057 | |
| E77 | 2 | JVM layout is spelled in ~13 places with two coordinate grammars; --ecosystems maven skips jvm ledger entries (B17); service_preflight grants downloads the backend then skips (B18); sbt roots with only project/build.properties get no crawl (B65). |
audit B17, B18, B65 | #1014 | in PR #1032 |
| E78 | 1 | pnpm readers and writers ignore ---, so a two-document pnpm 11+ lock gets the wrong document edited and success reported. |
audit B06 | #466 | in PR #1007 (pnpm workstream) |
| E79 | 2 | Hosted mode meeting a vendored package has 4+ outcomes per ecosystem (auto-takeover, refusal, Composer rewrites in place, NuGet socket-patch-<uuid> key collision). Target: one pre-rewrite decision. Depends on C34. |
audit B30 | #536 | filed #536 |
| E80 | 3 | The gitignore guard for vendored artifacts covers npm and sbt/Coursier only; Maven .jar and NuGet .nupkg are dropped by stock templates. |
audit B32 | #1061, #620 | filed #1061, #620 |
| E81 | 2 | Go writers ignore go.work and go env -w, and leave a read-only module-cache copy on Windows. |
audit B34 | #458, #393, #531, #343, #344, #391, #392, #549, #618, #346 | filed #458 and 9 symptom issues |
| E82 | 2 | Cargo dual-source registries and a user's own [patch.crates-io] aren't modelled. |
audit B35 | #480, #506, #679, #863 | filed #480, #506, #679, #863 |
| E83 | 3 | The Maven reactor re-implements the effective POM (properties, BOM imports, parents, profiles). | audit B36 | #459, #488, #513, #535, #550, #584, #622, #655 | filed #459 and 7 symptom issues; Maven is lower priority (maintainer triage) |
| E84 | 2 | Composer vendor-dir and global-home resolution ignore the global config and the XDG home. | audit §5 | #439, #586 | filed #439, #586 |
| E85 | 3 | Hosted NuGet forward and restore write packages.lock.json with LF, 2-space and no BOM; every other hosted JSON writer keeps the layout. |
audit B59 | #1068, #623 | filed #1068 |
| E86 | 3 | Vendored JVM fetches upstream artifacts and checksums only from Central or SOCKET_MAVEN_REGISTRY, ignoring mirrors and the build's repositories. |
audit B63 | #1069 | filed #1069 |
| E87 | 2 | VEX product detection has no Gradle or sbt probe, and scan --vex resolves the product only after writing. |
audit B64 | #1064 | filed #1064 |
| E88 | 3 | Small duplicates: hosted patch origins, max-severity ordering, "ecosystem filter is empty", and the inventory matching vendored-router refusal-code strings (#975). | audit §3.A, §3.B | #975 | to verify |
| E89 | 3 | Pure-wheel rule written 4× (inventory and recovery skip wheel_platform_from_filename); recovery's uv.lock scanner pairs a hashless pure wheel with another wheel's hash (executed twice). |
new finding | #1079 | filed #1079 |
Handed off: none yet.
Rejected / not a defect: E02, in part: the bun.lockb format-1 URL synthesized at bun_lockb.rs:235 is lock semantics and never fetched; the duplicate spellings were folded into #562.
Already fixed: none.
CLI layer, core infrastructure, agent mode, tests and docs (audit-core)
Last updated 2026-10-07T17:49Z · main @ 431b818 (October 7 reconciliation; duplicate rows merged)
| ID | P | Problem | Source | Issues | Status |
|---|---|---|---|---|---|
| C01 | 1 | Unbounded zip inflate in zip_bytes_match_after_hashes on committed .nupkg/.jar files and service archives. |
§1 #1 | #569 | fixed (#587); streams members, no cap per maintainer |
| C02 | 1 | ApiClient::new and plain_client() set no HTTP timeout, so scan, get and apply could hang in CI. |
§1 #2 | #570 | fixed (#581); the retry half is C15 |
| C03 | 1 | vendored_takeover ignored RevertOutcome.kept_artifact on a drift-keep. |
§1 #3; 2.4 | #568 | fixed (#708); revert_keeps_wiring refuses the takeover, #568 closed |
| C04 | 1 | Planted-binary spawn: vendor/pypi_hatch.rs ran a bare hatch in the project root. |
§1 #4; 7.3 | #613 | fixed (#617) |
| C05 | 1 | SOCKET_FORCE is bound to vendor --force, apply --force and self-update --force, so forcing a self-update also forces past hash checks. Merged C35: drop the deprecated spellings and decide embedded --vex. vendor --force promises a tolerance nothing implements (#923). |
§1 #6; R9; R10 | #615, #966, #923 | in PR #1021, #1031; decided 2026-10-07: SOCKET_FORCE binding removed (#615); v5 removes scan --apply/--vendor, get --no-apply, download, gc (#966); embedded --vex (Q2) undecided |
| C06 | 1 | get round-trips its arguments through DownloadParams and ..GlobalArgs::default(), which silently resets offline, patch_server_url and more. get also builds a fake ApplyArgs, and get and scan call each other. |
2.1; 2.3; R7 | rejected; resets inert on 045d7ec, cycle folded into C12 | |
| C07 | 1 | The URL builders disagree. When org auto-resolve fails, patches_path sends JSON calls to /v0/orgs/default/…, while binary_url and vendor_package_url send the same client to the public proxy. Telemetry has a fourth copy of this logic. |
7.2 | #648 | in PR #1041; decided 2026-10-07 (option 2): org resolved once per run into one route; failed auto-resolve → whole run on the proxy |
| C08 | 2 | Repo hygiene: a stray .github/actions/actions/cache/<sha>/.vscode/launch.json, a README that documents v5 but whose installer installs v4, and 39 references to a "DESIGN §" document that doesn't exist. (The dead CI path filters go to the CI janitor.) |
§1 #8; 8.5 J | #649 | filed #649; README part already fixed |
| C09 | 2 | There is no shared with_proxy_fallback helper: scan, get and vex each handle the proxy fallback, and apply, rollback, repair blob/diff downloads and vendor eject fetches have none (merged C39). Fix: the fallback moves into ApiClient. |
2.10 R2; new finding | #647 | filed #647 |
| C10 | 2 | Tracking: RunCtx { config, client, telemetry, lock }, built once in main. It would delete apply_env_toggles (flags written back into process env, called at 11 sites) and unblock removing most env-mutating #[serial]. |
2.5; R3 | #793, #794 | filed #793, #794; tracking #793, child 1 #794 |
| C11 | 2 | Tracking: split run_scan (1,540 lines on 045d7ec; JSON and human arms each dispatch all three modes) into discover → select → ModeBackend::consume → render. |
2.2; R5 | #843, #844 | filed #843, #844; tracking #843, child 1 #844 |
| C12 | 2 | Tracking: move engine code out of the CLI and into core behind one orchestrator over ProjectView: vendor_records_reusing, run_redirect_selected, ecosystem_dispatch.rs. Coordinate with E32. |
2.1; R11 | #894, #895 | filed #894, #895; tracking #894, child 1 #895; child 2 (command cycles) in PR #1043; 1,154 / 949 lines at 1c6c509 |
| C13 | 2 | Error codes are untyped. Target: a typed registry (enum Reason × Ecosystem) that generates the contract's code tables, plus a freshness test (~43 undocumented codes). One unparseable manifest gives five --json codes across commands (merged C52). |
2.8; 3.7 #8; 8.5 F | #930, #931 | filed #930, #931; tracking #930, child 1 #931 (manifest mapper) |
| C14 | 2 | Decide: one JSON envelope. scan and get still emit a bare-string error while the other commands emit {code, message}; exit-2 usage errors pick their --json channel per site (merged C53). |
2.8; R4 | #704 | in PR #1027; decided 2026-10-07: top-level error always {code, message}; every self-enforced exit 2 through one usage_error |
| C15 | 2 | There are three HTTP retry systems, and blob and diff fetches have none. A 206-line HTTP-date parser, two near-identical downloaders, and per-fetch or per-event clients round it out. Target: one retry + timeout primitive. | 7.2; R12 | #676, #677 | filed #676; #677 fixed (#889): vendor retries share parse_retry_after/jitter; blob/diff still no retry |
| C16 | 2 | Batch limits are split across crates. The CLI owns 500 / 100 / 256 KiB, and search_patches_batch documents a maximum of 500 without enforcing it. The in-memory engine keeps a third copy (default 100, no body cap). |
7.2 | #675 | filed #675 |
| C17 | 2 | Digest helpers are duplicated: inline hex::encode(Sha256::digest(..)) sites, compute-vs-validate sha256_hex copies, sha1_hex twice, SRI formatting inlined three times. |
4.4; 7.3 | #706 | filed #706; slice 1 merged as #865; slice 2 is the 6 files in PENDING_INLINE_DIGESTS (3 stale entries removed by #1016); sidecars/coursier.rs evades the text ratchet via a local digest() |
| C18 | 2 | There are four UUID grammars. client.rs has one, CLI lib.rs a byte-identical copy, path_safety.rs accepts lowercase only, and apply.rs accepts any alphanumeric plus -/_. |
7.3 | #705 | filed #705; the user-input half (2 of 5 grammars) in PR #1034; a fifth grammar in python_script.rs |
| C19 | 2 | Env truthiness has three vocabularies (core's "1"/"true" match kept correct only by apply_env_toggles), ~29 inline "empty means unset" reads, and six or more home-directory resolvers that disagree on Windows. |
7.3 | #727 | filed #727; home-directory half in PR #1038 |
| C20 | 2 | Tracking: purls have two builder families in utils/purl.rs, plus 42 hand-built pkg: strings and ~48 starts_with("pkg:<type>/") checks beside Ecosystem::from_purl. Purl equality is C63. |
6.4; 7.3 | #748, #747 | filed #748, #747; children 2–4 touch the ecosystem area |
| C21 | 3 | utils/fs.rs had six atomic writers (four boolean policies) plus a blocking stage copy. |
5.7; 7.3 | #728 | fixed (#858) |
| C22 | 2 | Telemetry has 19 near-identical public wrappers (17 track_*, 2 spawn_*), builds a new HTTP client for every event, and the CLI threads token/org through ~45 call sites (review said 125), resolved two ways. Target: one Telemetry handle with track(Event) and a shared client. |
7.5; R15 | #770 | filed #770 |
| C23 | 2 | Dead code: PatchSources::mem_blobs is never Some; VendorSource predicates are always true; group commit captures redirect-state.json, which nothing in its scope writes; the switched_off("group_commit") oracle path. |
7.4; 7.6 #3; 5.6 | #746 | filed #746; Pypi/LauncherCache channels are live (not dead) |
| C24 | 2 | Apply and rollback are mirror images: the verify types are identical, and fold_copy_result, the pnpm peer fan-out and the sidecar boundary are each written twice; the folds have drifted and both drop per-file records (#756). Target: one engine. |
7.4; 7.6 #4 | #771, #772 | filed #771; #772 fixed (#774), shared store_copies::fan_out; verify types + sidecar boundary remain |
| C25 | 2 | --download-mode diff, the default, re-downloads every blob on a cold cache, runs sequentially with no retry, and is the only user of qbsdiff. Making file the default is a decision; removing the duplicate fetch work is a refactor. |
7.4; R10 | #792 | in PR #1049; decided 2026-10-07 (option A, v5): delete the diff path, --download-mode and SOCKET_DOWNLOAD_MODE outright; patch/package.rs stays |
| C26 | 3 | apply.lock spends ~554 lines deleting the lock file on exit, and taking the lock replays the vendored group-commit journal, so lock-free vendor --check and vex misreport a crashed run (merged C46). |
7.4; new finding | #808, #809 | in PR #1030; decided 2026-10-07 (option C): the lock stays transient; journal layering is #809 |
| C27 | 3 | Agent-mode sidecars don't handle Maven files; Gradle-only projects were patched in ~/.m2. |
7.4 | #551 | already fixed (#646); Maven 3.9.11 ignores stale local .jar.sha1, Gradle half closed with #551 |
| C28 | 3 | socket.yml builds a hand-made YAML tree on serde-saphyr's event parser to read 8 keys. Target: serde with deny_unknown_fields. |
7.5 | rejected; the tree implements the contract's scoped YAML refusals | |
| C29 | 3 | The client.rs split (2.8K lines) into client, vendor_service and credentials; the debug-ordering machinery (HeldBack) has 45 call sites. |
7.2; 7.6 #8 | #871, #913 | filed #871, #913; #871 vendor-service move, #913 credentials move (~330 lines) |
| C30 | 2 | No shared test-support: binary() is defined in 103 files and git_sha256 in 86, there are 15 scrub_socket_env (14 bodies), xorshift is implemented four times, and the VEX helpers are forked. |
6.4; 8.5 D | #824, #823 | filed #824, #823; #823 slice 1 merged as #850 (common/hermetic.rs, 8 of 15 scrub_socket_env copies deleted); slice 2 is the other 7 |
| C31 | 3 | There are 235 test executables on 1c6c509 (review: 207; no [[test]] entries); the target is ~25. This needs C10 first. |
8.5 A | to verify; count confirmed, not filed | |
| C32 | 3 | 328 exact-sentence assertions should become --json/errorCode checks plus snapshots. Triage the covgap tests (32 files, 413 tests on 1c6c509), ~136 of which assert human text. |
2.5; 8.5 G/H | to verify; counts confirmed, not filed | |
| C33 | 3 | Tracking: CLI_CONTRACT.md (417 KB on 431b818; 332 KB at review) should be a checked reference (flags, env vars, codes, exit codes) plus ≤300 lines of prose, with freshness tests. |
8.3; 8.5 F/I | #948, #949 | filed #948, #949; tracking #948 (folds in #678, #930), child 1 #949 |
| C34 | 3 | Decide: the command model. A read-only scan, plus fix, undo, sync and check, with mode inferred from project state (or an explicit --mode for takeovers). This folds remove, rollback and vendor --revert. |
§4; 2.9; R6/R8 | decision pending; no decision issue filed yet | |
| C36 | 3 | Decide: the future of the self-update binary swap (2,350 prod lines; the only updater for the Windows zip). The agent-mode half moved to C55. | §6 Q2; 7.5 | #983 | rejected; #983 (2026-10-07): keep the --update swap and the notifier, harden them (retry, stall timeout, streaming, live test); #983 closed |
| C37 | 2 | Patch blob/diff downloads (fetch_binary) buffered the whole body with no cap. |
new finding | #571 | fixed (#607) |
| C38 | 2 | The public-proxy per-package fallback keeps a private cap of 10, ignoring SOCKET_API_CONCURRENCY, the proxy cap and the fd-limit rule; SOCKET_API_CONCURRENCY and SOCKET_WALK_THREADS are documented nowhere (merged C40). |
new finding | #614, #678 | filed #614, #678 |
| C41 | 2 | Hash case policy is per site: blob download compares case-insensitively and the validators accept uppercase, but agent-mode apply/rollback verify with exact ==, so an uppercase manifest hash never verifies. Vendored verify sites are split the same way. |
new finding | #707 | filed #707 |
| C42 | 2 | get writes a patch view's inline blobs to .socket/blobs/<hash> without verifying the hash, in place, overwriting existing verified blobs; the fetch path verifies and stages. It also hand-rolls base64. |
new finding | #726 | in PR #1042 (blob_fetcher::store_verified_blob) |
| C43 | 2 | With --manifest-path into another project, rollback, remove, repair, apply --check, vex, scan and get read the vendored ledger from --cwd while list, apply and vendor --check use the manifest's project; rollback locks one .socket/ and writes the other's ledger. |
new finding | #745 | filed #745 |
| C44 | 3 | The ecosystem-name parser is written three times: --ecosystems/SOCKET_ECOSYSTEMS need an exact, case-sensitive match, socket.yml patches.ecosystems trims and lowercases, and vendor::ecosystem_in_scope has its own lookup; -e NPM and -e "npm, pypi" exit 2. |
new finding | #773 | filed #773 |
| C45 | 3 | --download-mode is an unvalidated String, parsed late in fetch_stage/repair: a typo fails apply/repair with exit 1 and apply_failed/repair_failed (and scan/get only after saving the patch), while apply --check, rollback, list and vendor accept it; --vendor-source uses a clap parser (exit 2). |
2.7; R8 (narrowed) | #791 | filed #791 |
| C47 | 2 | 10 CLI test files spawned the binary with no SOCKET_* scrub; an ambient SOCKET_DRY_RUN=true turned 18 of 19 repair_vendor_e2e tests red. |
new finding | #823 | filed #823; 8 of 10 hermetic since #850; the last two are unblocked (#820, #849, #774 merged) |
| C48 | 2 | Child processes had no shared deadline; crawler probes called output() unbounded, so a hung gem shim hung scan. |
new finding | #845, #1067 | partly fixed (#886); pdm_site and the npm_dir git exchange keep hand-rolled deadlines (#1067) |
| C49 | 2 | Registry downloads used a 60 s whole-request deadline instead of ApiTimeouts' idle bound. |
new finding | #872 | fixed (#876) |
| C50 | 2 | Artifact GC has two retention policies: after_removal (rollback, remove) keeps active patches' beforeHash blobs for offline rollback, for_apply (repair, scan --prune) drops them; offline rollback then fails and names repair, which never fetches beforeHash blobs. cleanup_unused_blobs/_archives are dead. |
new finding | #893 | filed #893 |
| C51 | 3 | Agent-mode jar verification (jvm_jar::verify_member_bytes; apply, rollback, vex) buffers each patched member before hashing, while vendored zip_bytes_match_after_hashes streams since #587: 1,067 MiB vs 26 MiB peak RSS on a 1 GiB member. |
new finding | #914 | filed #914 |
| C54 | 3 | The contract documents status: paidRequired and errorCode paid_required for get and scan, but get emits legacy status: "paid_required" (two hand-written blocks) and scan reports only paidPatches; Status::PaidRequired is never constructed. |
new finding | #982 | filed #982 |
| C55 | 3 | Decide: the future of agent mode (CLI apply 2,963 + fetch_stage + repair; ~3.2K agent-only core; ~29 open agent-mode bugs). Only Deno and --global installs need it. |
§5; §6 Q2 | #1000 | rejected; #1000 (2026-10-07) chose option A: keep agent mode for every ecosystem for now, layout bugs fixed one at a time; #1000 closed |
| C56 | 2 | apply, apply --check and vendor treat any stat error on .socket/manifest.json as "no manifest" (noManifest, exit 0); repair/remove/rollback say manifest_not_found. Five metadata().is_err() probes bypass read_manifest's NotFound rule. |
new finding | #998 | filed #998 |
| C57 | 2 | remove <uuid> re-matches the vendor ledger by the raw identifier, so an older ledger generation is never reverted: manifest entry removed, vendoring kept, status: success. |
new finding | #999 | in PR #1035 |
| C58 | 3 | Standalone vex exits 2 (the global "usage error" code) on runtime failures: manifest_unreadable, manifest_not_found, write_failed, …; the same inputs exit 1 on apply --check, vendor --check, remove, repair and embedded --vex. PR #1027 would route them through usage_error. |
new finding | #1047 | filed #1047 |
| C59 | 1 | Credentials reach logs, --json, telemetry and third parties: hosted Composer keeps transport-options auth (B02), grant tokens and URL userinfo appear in warnings and debug output (B26), the VEX product @id carries git-remote credentials (B21). |
audit B02, B21, B26 | #399 | in PR #1026 |
| C60 | 1 | .socket containment: the symlink guard starts below .socket and guards deletes only (#887, B08); get writes inline blobs through a planted link (B24); agent writes follow links out of the package (B25); a new-file patch overwrites an existing file even under --strict (B23). |
audit B08, B23, B24, B25 | #887, #726 | in PR #1042; rollback of an overwritten file needs a pre-image store |
| C61 | 2 | CI gates pass while checking nothing: --cwd/--global-prefix/--manifest-path are never validated (B10), apply --check checks Go only (B27), hosted --json has no per-purl pin data (B12). |
audit B10, B12, B27 | in PR #1029; hosted exit code waits on #704 | |
| C62 | 2 | Four package-target grammars: get <name> fuzzy-substitutes and searches one version (B11), ignores --ecosystems (B56), and the UUID path skips socket.yml silently (B29, B57). |
audit B11, B29, B56, B57 | #453 | in PR #1034 |
| C63 | 2 | Purl equality has 6+ relations and 5 ownership matchers; NuGet case and PEP 503 spellings make scan --prune GC live entries (B20) and remove/rollback miss them (B73). Target: one PurlKey. |
audit B20, B73 | #553, #748 | in PR #1045 |
| C64 | 2 | ~12 hand-written path normalizers; home_dir falls back to a relative ~ (B66); the VEX repo-root walk has no HOME stop, ceilings or .git-file support (B22); FIFO-unsafe reads remain (B74). |
audit B22, B66, B74 | in PR #1038 | |
| C65 | 3 | Remedies prescribe a vendor --revert <purl> form that doesn't exist (B80); 8+ symlink refusal codes (B81); retired v4 spellings get misleading tips (B76); hosted remove prints its error twice (B77); five command-module cycles. |
audit B76, B77, B80, B81 | #894 | in PR #1043 |
| C66 | 2 | Test child processes send telemetry to production (B69); the e2e zero-tests guard covers Gradle only and the Windows npm leg is a no-op (B70). | audit B69, B70 | in PR #1046 | |
| C67 | 1 | Main went red on stale PENDING_INLINE_DIGESTS entries (two-sided ratchet, B01); no required checks, and push CI cancels most main verdicts (B28). |
audit B01, B28 | in PR #1018; digest entries fixed (#1016); a ruleset with required checks and a merge queue needs org admin | |
| C68 | 2 | 11 open -g (global install) bugs share one cause: global prefixes and shared caches are patched under per-project ownership. |
audit B33, B79 | #422, #423, #426, #435, #437, #443, #444, #450, #489 | filed #422, #423, #426, #435, #437, #443, #444, #450, #489; #1000 kept agent mode |
| C69 | 2 | Perf regressions have no owner: bun/hosted +110% (cause pinned), npm/hosted +15%, cargo and uv hosted per-patch re-parse. | audit B38 | #578, #993, #761, #836 | filed #578, #993, #761, #836; links to E27 |
| C70 | 3 | run_scan's human arm exits 1 when every query returns no patches; the JSON arms exit 0 (and preview GC and prune differently). |
audit B54 | #1062 | filed #1062 |
| C71 | 2 | scan reads a corrupt manifest as empty: --prune skips GC and reports success, the rollout counts recorded patches as new. |
audit B55 | #1063 | filed #1063 |
| C72 | 2 | Self-update and install.sh trust an unsigned SHA256SUMS from the same release. |
audit B67 | #1065 | filed #1065 |
| C73 | 3 | rollback --json counters count only the agent leg, so hosted and vendored failures report failed: 0. |
audit B75 | #1066 | filed #1066 |
Handed off (to the CI janitor): de-instrument coverage and drop the LTO docker-base build where it gates nothing, keeping the gating Linux tests and the per-PR Docker e2e on PRs; a reusable compat workflow; no per-leg compiles; dead CI path filters (review 8.2, 8.5 B/C/E).
Rejected / not a defect: C28: the hand-built socket.yml tree implements the contract's scoped YAML refusals (anchors, aliases, merge keys and tags refused only inside patches/projectIgnorePaths), which serde can't scope. C06: on 045d7ec the nested apply reads none of the reset fields except offline, which get/scan refuse up front; the cycle and fake ApplyArgs stay in C12.
Already fixed: C27 (#646).
Refactor routine (refactor, hourly, highest leverage first)
Last updated 2026-10-07T20:58Z · main @ 05ecc6e
In flight:
- No routine-opened PR is open. 24
arch-refactorPRs are open, soMAX_OPENis full; the routine re-ranks only. - Maintainer drafts (decided issues): #1021 (Decide: give SOCKET_FORCE per-command names so forcing a self-update doesn't also force apply and vendor #615), #1027 (Decide: one shape for the
--jsontop-levelerror(scan and get emit both a string and a {code, message} object) #704), #1030 (Decide: keep .socket/apply.lock transient, or give the lock a file that never has to be deleted #808), #1031 (Decide: warn on and then remove scan --apply/--vendor, and whether --vex stays embedded #966), #1036 (Decide: vendor single-module Maven poms through the suffixed-version jvm planner and retire the same-GAV <repository> wiring #973), #1041 (Decide: where patch API calls go when a token is set but the org slug can't be resolved #648), #1049 (#792), #1051 (#580). - October 7 campaign (duplicate business logic, one PR per seam; register rows in brackets): #1026 credentials [C59], #1029 trust signals [C61], #1032 JVM layout [E77, E69], #1033 VEX attestation [E72], #1034 target grammar [C62], #1035 supersede lifecycle [E71], #1038 paths and roots [C64], #1039 atomic takeover [E70], #1042
.socketcontainment [C60, C42], #1043 command cycles and UI text [C65, C12], #1044 governing locks [E75], #1045PurlKey[C63], #1046 test hygiene [C66], #1050 vendored liveness [E74], #1057 yarn grammar [E08, E76], #1058 pinned check [E73].
Merged:
- #1015: the vendored-reference scan reads every
VENDOREDrow and accepts the bare uuid dir. Issues #832, #958 (E61).8e521f9(+308 / −41). Left: deadeco == "maven2"arm incommands/vendor.rs. - #876: registry clients under
ApiTimeoutsthrough oneregistry_client_builder. Issue #872 (C49).e2300cc(+154 / −33). - #889: vendor-service retries share
api::retry(Retry-AfterHTTP-date, jitter). Issue #677 (C15 child 1).835601b(+209 / −38). Left: blob/diff fetches have no retry (#676). - Earlier: #886 (#845, C48 slice 1), #870 (#781), #865 (#706 slice 1), #858 (#728), #850 (#823 slice 1), #607 (#571), #602 (#592), #597 (#561, E01), #587 (#569, C01), #583 (E12), #581 (#570), #574 (#562), #572 (#563).
Queue (B bugs closed, U unblocks, D duplication removed, R risk; score = 3B + 2U + D − risk):
| # | Candidate | B | U | D | R | Score | Note |
|---|---|---|---|---|---|---|---|
| 1 | #990 (E24, child 1 of #989): one vendor::revert::finish with an explicit KeepPolicy for the 12 copied finish blocks |
0 | 1 | ≈12 | L | ≈14 | skipped: backend files changed by #1032, #1039, #1043, #1044, #1050, #1057 |
| 2 | #922 (E22, child 1 of #920): one VendorEntry::npm constructor for the 7 npm-family ledger tails |
0 | 1 | ≈7 | L | ≈9 | skipped: claimed; drivers changed by #1008 |
| 3 | #998 (C56): one NotFound-only manifest probe (5 metadata().is_err() copies) |
1 | 1 | ≈5 | M | ≈8 | skipped: commands/vendor.rs changed by open PRs; pairs with #931 and #1063 |
| 4 | #931 (C13 child 1): one manifest-read error mapper for every command | 1 | 1 | ≈2.5 | M | ≈5.5 | skipped: as #998 |
| 5 | #893 (C50): one artifact GC retention policy | 1 | 0 | ≈2 | L | ≈4 | next eligible when a slot frees |
Re-ranked 2026-10-07T20:58Z (unchanged: main still 05ecc6e, no PR closed since 18:56Z, the 8 maintainer drafts refreshed their heartbeats at 20:39Z). Earlier, the October 7 reconciliation: stale skip lists replaced (#657, #909, #940, #1015, #876, #889 merged). The campaign PRs above cover E08, E69–E77, C42 and C59–C67; don't start work on those rows.
Notes:
- The sandbox runs as root, so 4 core lib tests fail on main and on branches alike:
copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_maps_error_and_leaves_lock_untouched,pypi_requirements::wire_failure_rolls_back_already_written_files. redirect/pipenv.rs,vendor/pypi.rsandvendor/lock_inventory/vlt.rsaren't rustfmt-clean on main: format only your own hunks there. Checkrustfmt --checkon themaincopy before formatting a whole file.lock_inventory/mod.rsarchitecture_testsforbidhosted_patch_uuid*in a format file's model section. Origin-policy helpers go after the// ── registry view ──marker.redirect/mod.rsis a hot file (4 open PRs). Prefer candidates outside it until those land.CLI_CONTRACT.mdlives atcrates/socket-patch-cli/CLI_CONTRACT.md.- vlt registry semantics:
hydrateandSpecmemoize by id/spec and ignore options, so run each vlt case in a freshnodeprocess.scoped-registries[scope]wins for every segment;~~splits tonpm. Cite@vltpkg/dep-idhydrateTupleand@vltpkg/spec(registry ?? registries[default-registry-alias]); the packages download from npm. - Runs overlap: two runs started within minutes of each other on 2026-10-02. Claims and the status block kept them apart;
git pull --rebasethe ledger before writing. - Ledger and branch pushes need verified signatures (org ruleset). Commit with the session's default git identity; overriding
user.email(e.g. to a bot address) makes GitHub reject the signature. - Maintainer steering (2026-10-02, on #569 and #571): don't add size caps on trusted upstream data; stream instead of buffering.
- reqwest 0.12
ClientBuilder::read_timeoutis an idle bound (resets per chunk) and also bounds the wait for response headers;RequestBuilder::timeoutis total. rustfmt <file>also formats that file's out-of-line child modules (formattingcrawlers/mod.rsrewrotepython_crawler.rs). Checkgit diff --statafter formatting and restore any file you didn't mean to touch.cargo clippy --all-targets(and-p socket-patch-core --tests) already fails onmainfrom older lints in test code. CI's gate iscargo clippy --workspace --all-features -- -D warnings.- Windows CI checks out with CRLF. A test that scans source text for
\n-joined markers must normalize\r\nfirst: #602 failedtest (windows-latest)this way. - reqwest 0.12
Response::chunk()streams without thestreamfeature.BinaryBody::chunkreturnsimpl AsRef<[u8]>, so core needs no directbytesdependency. cargo test -p socket-patch-cli --test repairhas 2 root-only failures (repair_exits_zero_and_stays_quiet_when_lock_file_unremovable,repair_cleanup_failure_is_reported_in_json_and_silent_modes). They chmod a directory read-only.- Windows:
DirEntry::metadata().len()reports a stale (cached) size for a file another handle is still writing. Size live files withstd::fs::metadata(path)in tests. - CLI test targets: 145+ files spawn
socket-patchwith a bareCommand::new(binary());tests/spawn_env_hygiene.rskeepsPENDING_RAW_SPAWNS/PENDING_SCRUB_COPIESallowlists that fail on new and stale entries — drop a file from the list when you migrate it. utils::fswriters run on the blocking pool viarun_blockingsince #858: a test calling them needs a tokio runtime but either flavor works.#[cfg(test)] mod testsblocks often lean on the parent'suse sha2::…throughuse super::*: removing a production import breaks the test build (cargo test --lib --no-run), notcargo build. Add the import to the test module.- Bugbot may review the PR's start commit when a draft opens; check the review's commit SHA and re-trigger on the real head.
- go.mod's lexer treats
//as a comment anywhere, somodule a//bdeclaresa: don't expect//inside a token to be rejected. - A process-global
reqwest::Client(LazyLock) is unsafe in core tests: pooled connections stay bound to the tokio runtime that opened them, and each#[tokio::test]has its own runtime. Build per call through a shared builder instead. - The sandbox (root) also fails 3
covgap_commands_vendorstate-write-failure tests (chmod-based) on main and branches alike. - Probe spawns go through
utils::process::output_withinsince #886 (blocking; async callers wrap it inutils::fs::run_blocking). It nulls stderr; don't add a newtokio::time::timeout+kill_on_dropsite.
Part 2: CLI command layer and user experienceLast checked against main @ 431b818 on 2026-10-07 by the October 7 reconciliation (command cycles, takeover scope, envelope shapes and client sites re-checked; function sizes from
2.1 Headline numbers
2.2 God functionsSizes measured at
74 functions exceed 200 lines and 11 exceed 500.
Mode is three booleans ( The root cause is that output mode leaks into the engine. For example, 2.3 No service layer: commands call each otherCommand modules double as libraries and form a dense web:
2.4 Shared abstractions exist but are bypassed
2.5 Configuration flows through process environment
2.6 Structural duplicationA sliding-window copy-paste detector finds little literal duplication. The duplication is structural: helpers were extracted, but the pipelines around them were forked.
2.7 Flag and surface sprawl
2.8 Inconsistent JSON (verified against the binary)
2.9 UX: the command model is the real problem
A simpler command model:
That is 7 verbs instead of 9 visible + 2 hidden + 2 aliases + 3 hidden flag spellings, with one rule for mode ("whatever the project is, unless you say otherwise"). 2.10 Recommendations
New findings since the review
(The Generated by Claude Code |
Part 3: Hosted mode (redirect, hosted engine, upstream restore, Node addon)Last checked against main @ 05ecc6e on 2026-10-07 by audit-ecosystems (3.2
3.1 Size
The table predates Gradle (#646), sbt (#690) and That is about 27K production lines for hosted mode at the snapshot (more now), plus about 23.6K lines of inline tests and about 42K lines of hosted integration tests ( 3.2 How the redirect logic is organizedThere is no trait. Each rewriter is a free function with the signature
Adding an ecosystem to hosted mode means editing at least eight parallel tables:
Long functions.
In total there are 647 functions, 44 of them over 100 lines. Other symptoms:
Misplaced and dead code.
3.3 Two orchestrators: disk and in-memoryThe shared stages are good:
The warning-order row is a real divergence, and nothing catches it:
The parity suites exist only because there are two orchestrators: The memory engine also cannot reach several rewriters: vlt is always offline-withheld, maven and nuget raise Recommendation. Both paths converge on 3.4 Duplication with vendored, VEX and formatsA "one model per format" layer (
What already works, and is the template to copy: Module cycles (production
3.5 Upstream restore: rebuilding what was thrown awayCost. ~8.8K production lines at Why it exists. v5 dropped the redirect ledger. Yet every rewriter still computes Network dependencies: the npm registry, the crates.io sparse index, the Go proxy and Failure modes:
The open backlog shows the cost:
Assessment. Upstream restore is justified only where the original entry is a pure function of registry data: npm/pnpm/bun-text For uv, pylock, poetry, pdm, hatch, vlt, maven and bun.lockb, the module's own fallback (
There are two cheaper alternatives:
3.6 Per-package-manager features with poor complexity-to-value
3.7 Recommendations
New findings since the review
Generated by Claude Code |
Part 4: JavaScript lockfiles (npm, pnpm, yarn, bun, vlt)Last checked against main @ 431b818 on 2026-10-07 by the October 7 reconciliation (
4.1 Summary
4.2 Code per format
Shared npm-family infrastructure adds
4.3 Parser and splicer matrix
The question "which lockfile drives installs?" is also answered in at least eight places with different rules (the five below, plus
The modes disagree on policy. Vendored mode picks one flavor and warns about the rest. Hosted mode runs all five npm-family rewriters over every lock present. A repo with both 4.4 Verified duplicationpnpm v9 vs pnpm legacy. The drivers are now shared (#583): The vendor driver skeleton is copied seven times. npm_lock, pnpm (both dialects since #583), yarn-berry, yarn-classic, bun_lock, bun_binary and vlt (about 1,940 lines) all repeat the same sequence: Yarn berry project gates are written twice.
Small helpers that have already drifted apart:
CRLF policy is inconsistent for the same file family:
Five different answers to one question, and every one of them is a bug class (see the open-issue appendix). Across the whole crate there are five terminator rules (any-CRLF, first line, majority, CRLF-only re-expansion, refuse); on main the any-CRLF rule alone is written 14 times, in 4.5 Architecture defects
4.6 Complexity vs value
4.7 Target structureEach format exposes
Combined (A, B-drop, C-G, I): about 6-7K production lines and 8-10K test lines, before any vlt decision. New findings since the review
Generated by Claude Code |
Part 5: Vendored mode and the non-JS backendsLast checked against main @ 431b818 on 2026-10-07 by the October 7 reconciliation (5.5 zip inflate and 5.7 JVM scope re-checked). Earlier:
5.1 Size
Production lines per backend:
Framework files: 5.2 No backend traitThe only traits under
The signatures are close but not identical:
The CLI papers over the differences with two macros, 16 production sites enumerate the ecosystems (line numbers re-checked at
Inside backends there is a second dispatch layer: Ecosystem identity is inconsistent. The legacy single-POM Maven entry is The CLI reaches into backend internals: 54 distinct
These are hooks a trait should expose. The orchestrator
5.3 The ledger, and nine ways to undo a changeEach
"Record the original and restore it" is the right idea, but it is implemented about nine different ways:
The envelope around those mechanisms is copied too (re-checked at
Revert/restore/unwind code in the non-npm backends totals about 3,540 lines: gem 422, nuget 305, pypi 291, pypi_lock 254, uv 231, cargo 220, composer 205, and more. Back-compat costs:
5.4 DuplicationFormat parsers live in three homes ( XML: eight hand-rolled scanners and no XML crate.
Python:
Cargo:
Gem:
Go: Small helpers:
5.5 Security: zip inflate on committed artifacts (fixed, #587)
It runs on:
The codebase still has three different archive size caps on downloads: 512 MiB ( 5.6 Scaffolding left over from the removed local-build pathv5 removed local artifact building, but the scaffolding remains:
Value: negative. Delete it (about 250 lines, near-zero risk). 5.7 Complexity vs value
5.8 Target designtrait VendorBackend { // one impl per ecosystem, listed in a static REGISTRY
const ECO: &str;
fn artifact_shape(&self) -> Shape;
fn leaf_to_purl(..);
fn wiring_files(&self, v: &ProjectView) -> Vec<String>;
fn preflight(&self, v, pkg) -> Result<Option<PlannedDownload>, Refusal>; // = lock_text_refusal + service_preflight
fn plan(&self, v: &ProjectView, pkgs: &[Pkg]) -> Result<Plan, Refusal>; // pure, BATCHED
fn materialize(&self, archive, stage) -> Result<(), String>; // extract / tag / afterHash check
fn in_use(&self, v, e) -> Option<bool>;
fn recover(&self, e) -> Option<LockfileEntry>;
}
struct Plan { writes: Vec<FileWrite>, records: Vec<SpliceRecord { file, anchor, original: String, new: String }> }The engine owns:
Old Estimated saving: about 6–8K production lines of the ~44K in this slice (15–18%), and more in tests. Per-backend conformance tests collapse into one suite; for example, Risks:
New findings since the review
Generated by Claude Code |
Part 6: Discovery, inventory and VEXLast checked against main @ 05ecc6e on 2026-10-07 by audit-ecosystems (PyPI pure-wheel selection in
6.1 Size
The VEX stack is about 13K production lines tested by about 59K test lines: ~11K of core crawler tests and ~40K of CLI 6.2 Four discovery systems
How
Overlaps:
This could be one pass, because the per-format parsers are already mostly shared. What is duplicated is the classification and selection layer. One model would replace all of them: struct Instance {
purl: CanonicalPurl,
declared_in: Option<Rel>,
resolution: Registry { url, integrity, source_kind }
| Hosted { uuid, url, integrity, required }
| Vendored { uuid, artifact_rel, integrity }
| Other,
installed_at: Vec<PathBuf>, // filled in by locators (ex-crawlers)
}From that model:
6.3 Format × subsystem matrix (abridged)
Across the repo that is eight hand-rolled XML scanners, 4–5 independent walks of package-lock 6.4 Recurring helpers (confirmed)
Dead code:
6.5 VEX design
6.6 Crawlers
Are crawlers needed in hosted and vendored modes? Only as locators, not enumerators.
6.7 Target layoutRisks. Discovery is fail-closed, security-sensitive code. The golden snapshots and the ~40K lines of end-to-end tests are the safety net, so migrate one format at a time behind them. The New findings since the review
Generated by Claude Code |
Part 7: Core infrastructure and agent (in-place) modeLast checked against main @ 431b818 on 2026-10-07 by the October 7 reconciliation (7.3 spawn deadlines, the 7.4 safety model and the new 7.7 security boundaries). Owner: audit-core. Earlier:
7.1 Size
That is about 19.6K production lines. Comments are a large share of them: 25% of 7.2 API clientWhy
Three retry systems in one module:
The vendor policy also has three separate hand-written retry loops, plus a first-attempt/resume split that exists only to keep the request sequence identical under prefetch. Two near-identical downloaders ( Three URL builders with two different policies. Timeouts on the main paths (#581).
Batching is defined three times. The CLI owns the batch sizes (500 authenticated, 100 proxy, the 256 KiB body cap) and accepts any Other HTTP stacks keep TLS and proxy settings consistent but diverge on timeouts, retry and error formatting:
Target. One retry primitive (a classifier, a Retry-After parser, a timeout) to replace the four loops. That gives blob and diff downloads retry and timeouts for the first time. Then merge the downloaders and URL builders, and move the vendor service and credentials into their own files. 7.3 Duplicated utilities (verified)
7.4 Agent modeFootprint:
Counting the agent arms in The safety model is worth keeping, but it was not complete (see 7.7: the
The default Apply and rollback are mirror images.
So diff only saves bytes when a user commits
Sidecars (
Maven 3.9.11 doesn't verify the local repository's
Dead path (verified): 7.5 Features with questionable value
7.6 Recommendations
7.7 Security boundariesAdded on 2026-10-07 by the October 7 reconciliation; owned by
New findings since the review
Generated by Claude Code |
Part 8: Tests, CI, docs and distributionLast checked against main @ 431b818 on 2026-10-07 by the October 7 reconciliation (8.2 compile count, e2e legs, compat workflows, the coverage legs' gating role, and recommendation B). Owner: audit-core. Earlier:
PR #277 has already started cleaning up: it deleted 237,608 lines, including 136,809 lines of 8.1 Test suite architecture
Consolidation was started but not finished.
Duplicated helpers.
Coverage-chasing tests.
Exact human-text assertions. 328 Process-global env forces serialization. 8.2 CI costFor
About 516 jobs per push across all workflows:
8.3 Docs
User docs are lean, with rough spots.
8.4 Scripts and distribution
8.5 Recommendations
New findings since the review
Generated by Claude Code |
Appendix A: The open-issue backlog, mapped to architectureLast checked against Status on 2026-10-07: about 304 issues are open (~194 The repository had 88 open issues when this review was written. Most were filed between 2026-09-26 and 2026-10-01 by a bug hunt ( By ecosystem
By architectural cause
What the backlog says about the design:
Appendix B: Methodology and measurement notesSizing.
Function lengths come from brace matching over that lexed text: start line to closing brace. They can be off by a few lines but are not affected by braces inside string literals. Duplicates were found three ways:
CLI surface was measured on a debug build: CI numbers are from the GitHub Actions run for Verified by hand (not just reported by an area reviewer):
Not verified by execution: the per-area reviewers' "probable bug" readings beyond those listed (they are labelled as such in each part), and every LOC savings estimate. Those are engineering estimates, not measurements. Read-only: no code was changed. The review text lives under Generated by Claude Code |
|
[agent] 2026-10-02: the architecture routines are set up This discussion holds the October 2026 architecture review: the top post, plus Parts 2–9 above. It is also the running log of the refactoring work.
Generated by Claude Code |
|
[agent] 2026-10-02: architecture audit (ecosystems and formats) main @ Verified on main, each reproduced with a unit test run twice. The probe tests were not committed.
New finding: E49. There are four hosted-PyPI-URL grammars. Hosted Pipenv's Searched without filing:
Next backlog rows: E05 (cache crawls not project-scoped), E06 (non-FIFO-safe crawler reads), E07 (package-lock walks), E08 (yarn grammars), E09 (berry gates). Generated by Claude Code |
|
[agent] 2026-10-02: this discussion is now a living document
Generated by Claude Code |
|
[agent] 2026-10-02: architecture audit (CLI and core) main @ Verified on main. Every bug below was reproduced twice by execution. The probes were not committed.
New finding: C37. Searched without filing:
Living document: check lines set on Parts 2 and 7, tokens added for C02/C03, a C37 bullet under Part 7's new findings, and the cheap §0 numbers refreshed (209 test executables, Next backlog rows: C04 (hatch spawn, vs #442), C05 ( Generated by Claude Code |
|
[agent] 2026-10-02: architecture refactor run main @ Ranking.
The full top five is in the register's Queue. PR. #572 adds
Claimed: #563 ( Living document: added the Lessons: the four root-only test failures, and the rustfmt state of the files touched (see Notes). |
|
[agent] 2026-10-02T16:27Z: architecture refactor run
|
|
[agent] 2026-10-07: architecture audit (ecosystems and formats) main @ Reconciled.
Verified and filed: E26 → #971 (tracking), with #972 (child 1) and #973 (child 2, decision), both linked as sub-issues.
New finding: none met the bar this run. I searched:
False positives ruled out:
Living document:
Next backlog rows: E08 (yarn grammars; after #657), E14 (Pipfile.lock, still unproven), E24 (the nine revert mechanisms), E42 (embedded Generated by Claude Code |
|
[agent] 2026-10-07: architecture audit (CLI and core) main @ Reconciled: every register row holds its status. #876 (C49), #886 (C48) and #889 (#677, C15) are still open PRs, and nothing of ours merged. Backlog decomposed:
New finding: C54 → #982 (bug, contract drift). The paid-plan refusal is documented but not emitted.
Searched without filing:
False positives ruled out:
Living document:
Next backlog rows: C55 (agent-mode future decision), C34 (command-model decision; it assumes #966 is answered), then C31/C32 after #793. Generated by Claude Code |
|
[agent] 2026-10-07: architecture audit (ecosystems and formats) main @ Reconciled.
Verified and filed: E24 → #989 (tracking) and #990 (child 1, linked as a sub-issue).
New finding: E68, #988 (bug,
Searched without filing:
False positives ruled out:
Tooling note: the issue-write path wraps very long permalinks (about 150+ characters, e.g. Living document:
Next backlog rows: E08 (yarn grammars; after #657), E14 (Pipfile.lock, still unproven), E42 (embedded Generated by Claude Code |
|
[agent] 2026-10-07: architecture audit (CLI and core) main @ Reconciled: every register row holds its status. #876 (C49), #886 (C48) and #889 (#677, C15) are still open PRs, and nothing of ours merged. Backlog decomposed:
New findings (both from
Searched without filing:
False positives ruled out:
Living document:
Next backlog rows: C34 (command-model decision; it should wait for #966 and #1000), then C31/C32 after #793. Generated by Claude Code |
|
[agent] 2026-10-07: architecture audit (ecosystems and formats) main @ Reconciled.
Verified and filed: E30 → #1010 (tracking) and #1011 (child 1, linked as a sub-issue).
Verified and filed: E29 → #1012.
New finding: E69, #1014 (bug).
Searched without filing (survey of the new JVM code):
False positives ruled out:
Living document:
Next backlog rows:
Generated by Claude Code |
|
[agent] 20261007T1: maintainer triage decisions The maintainer decided nine open decision issues on 2026-10-07. The register rows and living-document passages now record them.
Also triaged: #580, the bun bench regression since #472. It is avoidable work ( Implementation PRs are pending for #973, #966, #808, #792, #704, #648, #615 and #580. #792 and #966 (and #615) edit the same migrating-to-v5.md table, so whichever lands second rebases. Generated by Claude Code |
|
[agent] 2026-10-07: architecture audit (CLI and core) main @ Reconciled:
Backlog:
New finding: C58 → #1047 (decision). Standalone
Searched without filing:
False positives ruled out:
Living document:
Next backlog rows: C34 (command-model decision), C32 (decompose the covgap triage), then C31 after #793. Generated by Claude Code |
|
[agent] 2026-10-07: October 7 reconciliation and campaign (maintainer) Main: Campaign PRs (all draft, label
Main went green with #1016 (stale digest entries) and #1022 fixed the docs drift in the repo. #1018 runs CI on a merge queue with per-SHA push concurrency (C67); the ruleset with required checks needs an org admin. pnpm, npm and bun issues stay with #1007, #1008 and #1009. Ledger changes:
Issues filed: #1061 (E80), #1062 (C70), #1063 (C71), #1064 (E87), #1065 (C72), #1066 (C73), #1067 (C48 remainder), #1068 (E85), #1069 (E86). Still open for maintainers: #704 exit policy (hosted exit code on unpinned patches), #966 Q2 (embedded Generated by Claude Code |
|
[agent] 2026-10-07: architecture audit (ecosystems and formats) main @ Reconciled.
Verified and filed: E31 → #1075 (tracking) and #1076 (child 1, linked as a sub-issue).
New finding: E89, #1079 (bug,
Searched without filing:
False positives ruled out:
Tooling note: the issue-write path drops the trailing footer from issue bodies (as on #1012); comments keep it. Living document:
Next backlog rows:
Generated by Claude Code |
|
[agent] 2026-10-07: architecture audit (ecosystems and formats) main @ Reconciled.
Verified and filed: E31 → #1075 (tracking) and #1076 (child 1, linked as a sub-issue).
New finding: E89, #1079 (bug,
Searched without filing:
False positives ruled out:
Tooling note: the issue-write path drops the trailing footer from issue bodies (as on #1012); comments keep it. Living document:
Next backlog rows:
Generated by Claude Code |
|
[agent] 2026-10-07: architecture audit (ecosystems and formats) main @ Reconciled.
Verified and filed: E31 → #1075 (tracking) and #1076 (child 1, linked as a sub-issue).
New finding: E89, #1079 (bug,
Searched without filing:
False positives ruled out:
Tooling note: the issue-write path drops the trailing footer from issue bodies (as on #1012); comments keep it. Living document:
Next backlog rows:
Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Note
[agent] This is a living document. It is the October 2026 architecture review of the socket-patch CLI, kept current as we work through the code. The scheduled routines rewrite a section when a refactor lands or they find a new problem. Every
E…/C…reference shows that problem's live status, taken from the register.Progress: 148 problems tracked · 18 fixed · 5 partly fixed · 1 already fixed · 31 in PR · 72 filed · 6 decision pending · 11 to verify · 4 rejected. On GitHub: 104 open and 26 closed
arch-auditissues · 24 open and 17 mergedarch-refactorPRs.Register: the first comment below tracks every problem with its issue and status.
Detail: Parts 2–9 follow as comments, and they are living too.
Log: after Part 9, every routine run posts an entry.
Work items: issues labelled
arch-audit· refactoring PRs labelledarch-refactorSteering: reply here or on an issue. The routines read maintainers' replies on their next run.
October 7 campaign: a one-day pass fixed the audit's critical defects and its duplicated business logic, one draft PR per seam, each deleting the duplicate copies it replaces: credentials redaction (Redact credentials from logged URLs, VEX product ids and hosted Composer locks #1026, C59), trust signals (Stop CI gates passing on missing paths, unverified agent patches and unreported hosted pins #1029, C61), JVM layout (Route JVM layout through one module and fix three JVM scope bugs #1032, E77), VEX attestation over PnP/bundled/deno copies (Stop VEX attesting over yarn PnP, pnpm bundled and deno.lock copies #1033, E72), one target grammar (Use one package target grammar for get, remove, rollback and the UUID shortcut #1034, C62), the supersede lifecycle (Replace a superseded patch generation's wiring on re-pin and remove #1035, E71), paths and repo roots (Share one path normalizer, home resolver and repository-root walk #1038, C64), an atomic takeover (Make the vendored-to-hosted takeover atomic #1039, E70),
.socketcontainment (Guard .socket links and agent writes with one containment helper #1042, C60), command cycles and remedy text (Break command-module cycles and share remedy and UI text #1043, C65), governing locks (Decide which lockfile governs installs in one table #1044, E75),PurlKey(Compare purls through one PurlKey type #1045, C63), test hygiene (Keep test children off production telemetry and fail e2e legs that run no tests #1046, C66), vendored liveness (Decide vendored-entry liveness through one discovery verdict #1050, E74), the yarn grammar (Move all yarn.lock parsing into formats/yarn and stop pinning non-registry copies #1057, E08/E76) and one pinned check (Decide whether a hosted patch is pinned through lockfile discovery alone #1058, E73). Main went green again with Fix main CI red on stale digest pending-list entries #1016 (stale digest entries); Run CI on the merge queue and stop cancelling main push runs #1018 adds a merge queue and per-SHA push concurrency so it stays green (C67). Maintainer decisions still open: Decide: one shape for the--jsontop-levelerror(scan and get emit both a string and a {code, message} object) #704 exit policy, Decide: warn on and then remove scan --apply/--vendor, and whether --vex stays embedded #966 Q2 (embedded--vex), C34 and E44–E47.Rendered 2026-10-07 20:56 UTC (ledger
9e9d018c) fromdoc/on thearch-audit/ledgerbranch. The original snapshot is inreview/2026-10/on the same branch.Architecture review of socket-patch v5: what to cut, combine, refactor and simplify
TL;DR
socket-patch is a product matrix implemented cell by cell. The matrix is 3 modes × 9 ecosystems × ~25 package-manager and lockfile generations × 6 operations (discover, apply/wire, verify, attest, revert, mode-takeover). There is no shared abstraction on any axis:
Each cell is hand-written text surgery. As a result, the same file format is parsed and spliced two to five times with different rules, and those rules have already drifted. For example, there are five different CRLF policies for the npm lockfile family alone.
Most of the open bug backlog has one shape: "reported success, but the build consumes unpatched code". Of the 88 issues open at the snapshot, roughly 29 were a success or a
not_affectedVEX attestation that the installed bytes don't back up (about 46 such false attestations are open on 2026-10-07; Stop VEX attesting over yarn PnP, pnpm bundled and deno.lock copies #1033 and Fix VEX attesting beside an unpatched same-lock copy (#935, #938, #939) #940 address the largest groups). Another 15 were discovery missing what the package manager actually installed. The root cause is architectural:Support breadth has outrun the architecture. The long tail is disproportionately expensive:
bun.lockbA support-tier policy would let the core get simpler.
The code is large for what it does, and much of the size is duplication and scaffolding.
9c43dfc(~118K at the snapshot, plus 35K comment lines then).run_scanalone is 1,540 (on045d7ec).redirect/mod.rsondb83f01(17.5K at the snapshot).E30· filed Tracking: split patch/redirect/mod.rs into per-ecosystem modules and sibling test files #1010, Move patch/redirect/mod.rs's inline test modules into sibling test files #1011; tracking Tracking: split patch/redirect/mod.rs into per-ecosystem modules and sibling test files #1010, child 1 Move patch/redirect/mod.rs's inline test modules into sibling test files #1011 (inline tests out)E24· filed Tracking: revert every vendored backend through one record-revert engine instead of nine hand-written mechanisms #989, Finish every vendored revert through one shared helper with an explicit keep policy instead of 12 copied finish blocks #990; tracking Tracking: revert every vendored backend through one record-revert engine instead of nine hand-written mechanisms #989, child 1 Finish every vendored revert through one shared helper with an explicit keep policy instead of 12 copied finish blocks #990 (finish step ×12, four keep policies)We estimate 25–35K production lines (20–30%) and 50K+ test lines could go. Roughly 20K of that comes from consolidation that keeps every capability; the rest comes from the support-tier and product decisions in §5.
The user model is harder than it needs to be. It has:
scanwriting lockfiles by default;scanperforms the documented "mode takeover" and switches vendored npm, Cargo, Go, PyPI and Gradle-built Maven packages back to hosted (made atomic in Make the vendored-to-hosted takeover atomic #1039, E70);remove,rollbackandvendor --revertas three ways to undo.A seven-verb model (
scanread-only,fix,undo,sync,check,list,vex) with mode inferred from the project would cover everything (§4).There are a few real defects to fix now (§1), regardless of any refactor:
mainred for every PR (fixed, Fix main CI red on stale digest pending-list entries #1016; a merge queue follows in Run CI on the merge queue and stop cancelling main push runs #1018).The October 7 audit added credential leaks (C59),
.socketcontainment (C60) and the non-atomic takeover (E70); each has a PR.SOCKET_FORCE(Decide: give SOCKET_FORCE per-command names so forcing a self-update doesn't also force apply and vendor #615, PR Remove the SOCKET_FORCE env binding; --force is flag-only (#615) #1021) and thebun.lockbregistry override (E02, fixed by Resolve vlt registry bases through one shared function (#562) #574) are no longer open defects.0. The numbers
socket-patch-core+socket-patch-clion9c43dfc(2026-10-06; the same script gives 117.8K at the snapshot, which the review reported as 117.6K + 34.8K comment + 9K blank). The growth is mostly the Gradle landing (#646)#[cfg(test)]code insrc/9c43dfc(~197K at the snapshot)crates/*/tests)9c43dfc, 2026-10-06; ~255K at the snapshot)patch/redirect/mod.rs: 21,936 lines atdb83f01(2026-10-07; 7.6K production, 14.3K inline tests; 17,517 at the snapshot, 6.2K production then)1c6c509(61 / 9 at the snapshot:run_scan1,540 on045d7ec, now 1,577;rollback::run984,vendor_records_reusing962,run_redirect_selected836,remove::run797,get::run635, memoryengine604, …)SOCKET_*names in source); 156 documentederrorCodes; ~570 code-like strings in source--helplist --helplists 27 options, most of which do nothing forlisttestis the 28-minute critical pathCLI_CONTRACT.md431b818(2026-10-07; 415 KB atc5be5d1, 332 KB at the snapshot); the longest line is 12,077 characters atc5be5d1bughunt, ~94arch-auditbefore nine newarch-auditissues #1061–#1069). At the snapshot: 88, filed mostly in the last 5 days by a bug hunt; JS 26, JVM 22, Python 18, Go 6, Cargo 5, NuGet 5, Ruby 3, Composer 31. Fix now (small, independent of any refactor)
zip_bytes_match_after_hashesnow streams each member through the Git SHA-256 reader with an 8 KiB buffer and checks the declared length against the bytes read, instead of inflating it into aVec(#587). The maintainer ruled that this data is trusted not to be too big, so the goal was streaming, not a cap.C01· fixed (#587); streams members, no cap per maintainerApiClientreqwest clients now takeapi::retry::ApiTimeouts(10 s connect, 60 s idle read), and a stalled JSON body reportsApiError::Network(#581). Blob/diff downloads still have no retry.C02· fixed (#581); the retry half is C15vendored_takeovernow checksrevert_keeps_wiring(kept_artifact, drift skips, residual references) after each revert and refuses the purl while keeping the ledger entry, like every other revert caller (#708).dispatch_revert_onedirectly rather thanVendoredBackend(Part 2.4).C03· fixed (#708);revert_keeps_wiringrefuses the takeover, #568 closedhatchthroughutils::process::resolve_tool_withand spawns it withcommand_for, so ahatchplanted in the scanned repo no longer runs (#617). On0d302dcno production bare-nameCommand::new("<tool>")remains.resolve_tool(Part 7).C04· fixed (#617)nuget.configthroughformats::nuget::parse_config, so commented-out<add key>entries are ignored (#597). The vendorednuget_feed.rsreader remains.formats::nugettoo (E10, #594).E01· fixed (#597)SOCKET_FORCEis bound to three unrelated flags (decided)vendor --force,apply --forceandself-update --force. Exporting it to force a self-update also forcesapply/vendorpast hash checks.SOCKET_FORCEbinding is removed and--forceis flag-only; the change is in PR #1021.C05· in PR #1021, #1031; decided 2026-10-07:SOCKET_FORCEbinding removed (#615); v5 removesscan --apply/--vendor,get --no-apply,download,gc(#966); embedded--vex(Q2) undecidedbun.lockbregistry override (fixed)NPM_REGISTRYspellings are now one helper, and vlt's tworegistry_basecopies are one (#574). The format-1 URL thatbun_lockb.rssynthesizes is lock semantics and is never fetched, so that part is not a defect.E02E03: 2 fixed--json, telemetry and the patch host (October 7)transport-optionsauth (#399); grant tokens and URL userinfo appear in warnings and debug output; the VEX product@idcarries git-remote credentials.utils::redactfor every URL shown or logged (PR #1026).C59· in PR #1026.socketlinks and agent writes escape the project (October 7).socketand guards deletes only (#887);getwrites inline blobs through a planted link (#726); agent writes follow links out of the package.C60· in PR #1042; rollback of an overwritten file needs a pre-image store--dry-runpredicts success.E70· in PR #1039; dry-run residuals #668, #744, #891, #979 open.github/actions/actions/cache/<sha>/.vscode/launch.json(accidentally committed in #358); 2 dead CI path filters (CI janitor); 39 references in 20 files to a "DESIGN §x.y" document that isn't in this repository. The README now says plainly that its installer selects the latest release (verified on045d7ec).C08· filed #649; README part already fixed2. The big picture: why the code is the size and shape it is
2.1 No abstraction on any axis of the matrix
formats/layer. Its module doc promises "entry grammar, key rules, version sniff and planners" per format; only pnpm, cargo, gem, composer and bun are partly there.parse → model (with byte spans) → entries() / wired_refs() / splice(edits), used by every mode. Today package-lock has 4 entry walks, yarn has 5 copies of asplit("\n\n")+regex grammar beside the shared block scanner, there are 8 hand-rolled XML scanners (no XML crate), Cargo.toml has a regex scanner andtoml_editinside one rewriter, and poetry/pdm lock code are near-twins.run_scan, referenced 91 times; JSON and human arms that each re-dispatch all three modes.trait ModeBackend { plan, consume, revert, verify }, with rendering only at the end.vend!,vend_installed!). The ecosystem list is enumerated at 16 production sites. Nine different revert mechanisms (~3.5K lines).trait VendorBackend+ a registry + one generic splice-record revert engine. The JVM planner (jvm/mod.rs) already is this design; copy it.Vec<Box<dyn Fn>>. Results flow through aRewriteResultwith 27 per-ecosystem uuid sets (20 at the review) and a 25-outcomeconfirm()if-chain.E31· filed #1075, #1076; tracking #1075, child 1 #1076 (one report map, mechanical) Eight parallel tables must be edited to add an ecosystem.trait HostedRewriter { drives(), rewrite() -> Outcome { per_dep: Map<Uuid, DepStatus> } }vex::discover) and a ledger supplement. They are merged by fabricatingCrawledPackages with a fakenode_modules/<name>path for every ecosystem.Inventory { instances: purl × declared_in × resolution (Registry/Hosted/Vendored) × installed_at }. Crawlers become locators.args.rs:559 apply_env_toggles) so core can read them. Its doc comment records a bug where telemetry sent a Bearer token to the wrong host. This also forces 993#[serial]test attributes (plus 185 insrc).C10· filed #793, #794; tracking #793, child 1 #794RunCtx { config, client, telemetry, lock }built once inmain.Layering is inverted and cyclic (production
crate::X::reference counts):Other examples:
crate::vendor::*, sovendor/has become the codec library.Ecosystemandcanonicalize_pypi_namelive incrawlers/(the latter is imported by 29 files).formats/pnpm/hosted.rsimports the hosted engine'sRewriteResult.The CLI holds engine code.
vendor_records_reusing(962 lines) is the vendored orchestrator.run_redirect_selected(836) is the disk hosted orchestrator, written a second time in core'shosted/memory(~1.3K lines of orchestration).ecosystem_dispatch.rsis 816 lines of crawler fan-out.Commands call each other as libraries.
get↔scancycle.getbuilds a fakeApplyArgsand callsapply::run_locked.DownloadParams→..GlobalArgs::default(). On045d7ecthe reset fields are inert: the nested apply reads none of them exceptoffline, whichgetandscanrefuse up front.2.2 The correctness model: "wired" is treated as "consumed"
The tool decides that a patch is applied, and VEX marks it
not_affected, mostly from what it wrote. It does not check what the package manager will install:PatchedRef::lockfile_basis_oklets an attestation stand with no installed bytes;Every package-manager behavior outside that model becomes a silent false negative. The open backlog, by title, includes:
go.workreplaces overriding go.mod (A user replace in go.work silently overrides the Socket go.mod replace: Go apply and vendor report success and VEX attests not_affected while the build links the user's target #393);vexattests a hosted patch as not_affected (verified) while the installed copy under node_modules/.bun is still unpatched (v5 regression) #405);deno.locktaking precedence over package-lock (Hosted npm pin in package-lock.json is attested by VEX in a Deno project, but deno.lock keeps installing the unpatched registry copy #406);--system-site-packages(In a--system-site-packagesvenv, pip keeps the base interpreter's unpatched copy after a hosted rewrite, no stale-install warning fires, andvexattests it as patched #409);<repository>or amirrorOf *mirror serves the same GAV, and VEX still attests #263);BUNDLE_GEMFILE(Gem hosted redirect andsetupignoreBUNDLE_GEMFILEfrom.bundle/config, so they wireGemfilewhile bundler loads the configured manifest unpatched (VEX andsetup --checkstill pass) #390);gems.rbbesideGemfile(Vendored gem mode wires Gemfile when gems.rb is also present, so bundler installs the unpatched gem while vex attests it #341);inBundlecopies (npm VEX attests not_affected while a bundled (inBundle) copy of the same package@version stays unpatched #325);globalPackagesFolder(Agent-mode NuGet apply patches ~/.nuget/packages instead of the project's configured globalPackagesFolder / RestorePackagesPath, reports success, and VEX attests not_affected #397).Patching each case individually makes the model ever larger. Structural options, which can be combined:
Verify what the package manager consumes, not what we wrote.
--allow-wired-basisflag.not_affectedis worse than no statement, because the whole point of VEX is that scanners trust it.Ask the package manager instead of re-implementing it wherever possible:
poetry env info -p,pipenv --venv,uv python find;npm query/npm ls --json,pnpm list --json;go list -m -json all,cargo metadata;mvn dependency:list,dotnet list package --include-transitive.Today the crawlers re-implement Poetry's and Pipenv's venv-name hashing, npm/pnpm/yarn/bun global-prefix discovery, and the pnpm store layout, and those re-implementations are the source of the agent-mode discovery bugs (Agent-mode scan misses Poetry's venv for nameless non-package-mode projects, [project].name overrides, and in-project = false with a stray .venv, and still exits 0 #327, Windows agent-mode scan never finds Poetry's default out-of-tree virtualenv, so patches are skipped and the scan exits 0 #329, Agent-mode scan skips Pipenv's out-of-tree venv when the project has a stray venv/ directory or a .venv with PIPENV_VENV_IN_PROJECT=0, and still exits 0 #334, Agent mode ignores pnpm's virtualStoreDir: transitive dependencies are reported package_not_installed with a custom virtualStoreDir or the global virtual store #362, Bun isolated linker: transitive packages under node_modules/.bun are "not installed" in agent mode, and scan --mode agent exits 0 with them unpatched #366, Deno nodeModulesDir: transitive npm packages under node_modules/.deno are "not installed", and apply/scan exit 0 leaving them unpatched #373, Agent-mode scan patches the activated VIRTUAL_ENV even when PIPENV_IGNORE_VIRTUALENVS or PIPENV_ACTIVE tells Pipenv to ignore it, leaving the Pipenv venv unpatched with exit 0 #384).
Fail closed on unmodeled configuration. Detect the knobs that change resolution (
go.work,gradle.lockfile,BUNDLE_GEMFILE,virtualStoreDir/enableGlobalVirtualStore,install-strategy=linked,repositoryPath/globalPackagesFolder, mirrors) and refuse or warn instead of reporting success.Make "verify after install" a first-class step (
socket-patch check) that CI runs after the package manager, with a non-zero exit when what was installed doesn't match what was wired.2.3 Hosted rollback rebuilds data it threw away
v5 dropped the hosted ledger, so
rollback/removereconstruct the original lock entries from the network. That is ~7.4K production lines across about nine upstream sources, including a Socket endpoint that may download the whole upstream tarball. Meanwhile, every rewriter already computesFileEdit { original, new }, and production discardsoriginal, except in a Composer hint.Failure modes:
SOCKET_NPM_REGISTRY);bun.lockbalways refused.That is 13 open rollback/takeover bugs (#271, #331, #382, #385, #407, #408, #410, #411, …).
Options:
resolved+integrity, cargocksum, go.sum, gem/composer/nuget hashes; ~2.2K lines), and refuse the rest with an exactgit checkout -- <file>or relock command.Either removes ~3.3K production lines.
2.4 Machinery that compensates for the per-package call model
Vendored backends are invoked once per package, and each call re-reads, re-parses and durably re-writes the same lockfile and ledger. Several mechanisms exist to make that fast and crash-safe again:
group_commit.rs(1,059 lines), a process-wide virtual filesystem that intercepts everyutils::fsread and write;durability.rs;prestage.rs;api/vendor_prefetch.rs;ParseMemostatics;ledger_snapshots.rs, the schema-v2 delta encoding added because whole-file snapshots bloated ledgers by tens of MB.Together that is ~3K production lines. Backends written as pure batched planners (
plan(view, pkgs) -> {writes, records}, asjvm/already does) would retire most of it.2.5 Process smell: nothing gets deleted
Several patterns show code that outlived its purpose:
--vendor-source(one valid value),VendorSource/PackageSource(one variant each),PatchSources::mem_blobs(neverSome;C23· filed Delete PatchSources::mem_blobs, the single-variant VendorSource predicates, the redirect-state group-commit capture and the group_commit switch-off oracle #746;Pypi/LauncherCachechannels are live (not dead)),lock_inventory/wired.rs(no production caller), pre-v5 redirect-ledger readers.v5.0annotations in the contract.Suggested norms:
3. Ranked recommendations
C = cut, M = combine/merge, R = refactor, S = simplify. LOC are production lines unless noted. Risk: L/M/H.
formats/(package-lock → yarn → XML for NuGet/Maven/Gradle → requirements → Cargo.toml → Pipfile → pnpm single grammar), shared by hosted, vendored, upstream, inventory and VEX. Neutral types (Edit,Warning,LockfileEntry) move intoformats, which breaks the cycles.E07–E20: 2 fixed · 1 partly fixed · 3 in PR · 6 filed · 1 to verify · 1 missingVendorBackendtrait + registry + one generic splice-record revert engine, with backends as batched pure planners (the JVM pattern). Legacy ledger kinds are adapted at load time.E21E22E23E24E25E27: 1 in PR · 4 filed · 1 to verifyInventoryfusing lock inventory, wiring discovery and the ledger supplement. Crawlers become project-scoped locators, with no whole-machine cache enumeration (~/.m2,~/.nuget/packages,$CARGO_HOME,GOMODCACHE). Renamevex::discovertoinventory::wiring.E05E36E37E38E39E40E41: 6 filed · 1 to verifyE33E45: 1 decision pending · 1 to verifyDiskSnapshot→MemoryProject→redirect_root(view, selected, api, hooks)); parity suites become ordinary tests. Decide the napi addon's fate: if depscan adopts it, delete the TS rewriters; if not, delete the addon,hosted-bundleand the memory-only branches.E32E44: 1 decision pending · 1 to verifyrun_scaninto discover → select → consume → render; aRunCtxbuilt once; a service layer in core so commands stop calling each other;HostedRewriter+Outcome; mechanical split ofredirect/mod.rs.C10C11C12E30E31: 5 filedscan,fix,undo(foldsremove+rollback+vendor --revert),sync,check; mode inferred from project state; per-command flags; delete deprecated spellings.C34· decision pending; no decision issue filed yetscan/get/rollbackstill emit an untypederror: a bare string on some paths, a{code, message}object on others; decided #704, option 1: always{code, message};C14· in PR #1027; decided 2026-10-07: top-levelerroralways{code, message}; every self-enforced exit 2 through oneusage_error); a typed code registry (enum Reason × Ecosystem) that generates the contract's code tables, with a freshness test.9c43dfc) and 1 phantomC13C14: 1 in PR · 1 filedbun.lockbwrite support → refuse with remedy; vendored pnpm 7/8 → refuse (or a dialect of v9); vlt pre-1.0 encodings; Maven single-POM backend retired; single poms go through thejvm/planner (decided #973).E26E47: 1 in PR · 1 decision pending--download-modeand the diff path (decided #792, v5):diffre-downloads every blob anyway on a cold cache; delete the diff machinery, the flag andqbsdiff.C25· in PR #1049; decided 2026-10-07 (option A, v5): delete the diff path,--download-modeandSOCKET_DOWNLOAD_MODEoutright;patch/package.rsstays--vendor-source,VendorSource,PackageSource,vend_installed!,mem_blobs,lock_inventory/wired.rs, dead vlt ledger helpers,save_redirect_state+ its group-commit entry, the empty Deno extractor, theswitched_off("group_commit")oracle path.E28E41C23: 3 filedformat!("pkg:…")and 24 prefix checks in production code); one digest/SRI helper set (fixing thesha256_hexname collision: one copy validates, three compute); one line-ending policy; one env-truthiness vocabulary (there are three); one UUID grammar (there are four).C15C17C18C19C20E16: 6 filed--vex(15 flag instances on 3 commands, ~600 lines of glue, plus bypass sets that couple VEX correctness to each caller) →fix && vex -O.E42C05: 1 in PR · 1 decision pending (spellings half decided #966)allow-remote(re-implements npm'siniand config layering, ~900 lines), pnpmtrustLockfile(~450; three open corruption bugs), the vlt warm-tree heal (installed-tree surgery in a lockfile-only mode), parallel rewriter groups (benchmark them or drop them).E34· to verify; dead vlt ledger helpers in #782 (E58)track(Event)+ a shared client instead of 19 wrappers and ~45 token/org call sites.C22C36: 1 filed · 1 rejectedRunCtxfirst, to drop the env-mutating#[serial]); asocket-patch-test-supportcrate (binary()is defined in 103 files,git_sha256in 86, 14 divergentscrub_socket_env, and 10 test files with no env scrub at all); retire the oracles; triage covgap; snapshots instead of 328 sentence assertions.C30C31C32E35: 1 filed · 3 to verifydocker-basebuild where it gates nothing, keeping the gating Linux tests and the per-PR Docker e2e on PRs; PR e2e 157 legs → ~50 boundary versions; reusable compat workflow; no per-leg compiles; required checks plus a merge queue (#1018).C67· in PR #1018; digest entries fixed (#1016); a ruleset with required checks and a merge queue needs org adminecosystems.mdand version history intodocs/migrating-to-v5.md; decoupledocs/testingfrom validation scripts.C33· filed #948, #949; tracking #948 (folds in #678, #930), child 1 #949Estimated total: ~25–35K production lines (20–30%) and 50K+ test lines. About 20K is pure consolidation (recommendations 1–3, 6, 8, 10–12, 14); the rest depends on the tier and product decisions (recommendations 4, 5, 9, 13; the self-update half of 15 was decided as keep, #983). Agent mode stays for every ecosystem (#1000), so its ~10K is not on the table. The per-recommendation numbers overlap: for example, recommendation 1 shares work with 2 and 9.
4. User experience: a simpler model
Today a new user has to learn:
scan --pruneandscan --globalbecome report-only;get --save-onlyandget --globalbecome agent mode;get -g xpatches files in place whilescan -gonly reports;scanmutating lockfiles by default;remove,rollback,vendor --revert;repair(aliasgc), filed under "Agent mode" in help but also repairing vendored artifacts, andscan --prune;--help, most of them no-ops for that command;Proposed command model:
socket-patch scanscan --dry-run,listpartiallysocket-patch fix [TARGET…] [--mode hosted|vendored|agent]--modechooses it for a fresh project, and switching an existing project's mode requires--modeexplicitly.scan(write),get,vendor(eject)socket-patch undo [TARGET…] [--keep-state|--forget]rollback,remove,vendor --revertsocket-patch syncapply,repair/gc,scan --prunesocket-patch checkvendor --check,apply --checksocket-patch list,socket-patch vexOn top of that:
errorCode;This is a MAJOR change. Because v5 is still a prerelease, now is the cheapest time to make it.
5. Support tiers (product decisions needed)
bun.lockbwriting (hosted + vendored)bun.lock. The codec writes a privatesktpnrmmarker into an unused slot of the user's lockfile.bun install --save-text-lockfile --frozen-lockfile --lockfile-only; keep a ~300-line read-only parser if inventory needs it.--frozen-lockfileonly passes at the original checkout path, which undercuts the point of vendoring.PnpmDialect.maven_repo.rsintojvm/(Shape::Single); one XML scanner; scope the crawler to project dependencies, not all of~/.m2. Consider labelling hosted Maven/Gradle "beta" until the backlog is under control.C55· rejected; #1000 (2026-10-07) chose option A: keep agent mode for every ecosystem for now, layout bugs fixed one at a time; #1000 closed"private": true, never released, built and smoke-tested on every PR.C36· rejected; #983 (2026-10-07): keep the--updateswap and the notifier, harden them (retry, stall timeout, streaming, live test); #983 closed6. Suggested sequencing
setupcommand (Composer setup rewrites a CRLF composer.json as LF (and un-escapes \/ and \uXXXX), so setup --remove does not restore it byte-for-byte #351, Gem hosted redirect andsetupignoreBUNDLE_GEMFILEfrom.bundle/config, so they wireGemfilewhile bundler loads the configured manifest unpatched (VEX andsetup --checkstill pass) #390, npm apply exits 1 when every patch targets a platform-skipped optional dependency (fsevents, @esbuild/*), so the setup hook fails npm ci and npm install on other OSes #403).formats/codecs, one format per PR, each PR deleting the duplicate walks it replaces.RunCtx, which also unblocks the test-binary merge.VendorBackend+ revert engine.HostedRewriter+Outcome+ theredirect/mod.rssplit.Inventory.--vex.fileas the default download mode.Questions for owners:
bun.lockb, pnpm ≤ 8 and Gradle usage, to set tiers?.socket/in hosted mode" a hard requirement?Generated by Claude Code
All reactions