Skip to content

Fix repair leaving npm vendor dirs uncommittable (#1581) - #1656

Merged
Mikola Lysenko (mikolalysenko) merged 6 commits into
mainfrom
agent/fix-repair-npm-uuid-metadata
Oct 22, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 6 commits into
mainfrom
agent/fix-repair-npm-uuid-metadata

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1581

Summary

socket-patch repair now restores the .socket/vendor/npm/<uuid>/.gitignore
and .gitattributes for every npm-family vendored entry (npm, pnpm, yarn,
bun text, bun.lockb, vlt), both when the artifact is healthy and after a
redownload. It then checks that git would actually commit the artifact. A
rule that the uuid .gitignore can't override (such as .socket/) now
fails with vendor_artifact_gitignored, the same refusal vendor gives,
on every run until the rule is fixed.

Root cause

repair only rewrote the uuid-dir metadata for vlt and JVM entries
(commands/vendored_backend/repair.rs). The npm tarball flavors only got
it from a vendored scan (npm_common.rs keep_pack_committable). So:

  1. A project vendored before Vendored Bun tarballs go into Git LFS when .gitattributes tracks *.tgz with LFS, so a checkout without LFS fails bun install --frozen-lockfile #1541 kept the legacy * -text
    .gitattributes. A repository *.tgz filter=lfs rule still applied,
    and the repaired tarball went back into LFS on the next commit.
  2. When the whole <uuid>/ dir was lost, repair restored only the .tgz.
    With a stock *.tgz ignore rule, git ignored the redownloaded tarball,
    so "commit it" committed nothing (the second symptom reported on repair leaves a pre-#1541 root <uuid>/.gitattributes as * -text, so the repaired Bun tarball goes back into Git LFS on the next commit #1581).

Changes

Test evidence

Red → green. The new CLI tests were run against origin/main with only
the test file changed, in a separate worktree:

cargo test -p socket-patch-cli --all-features --test in_process_vendor repair_
main:   repair_of_a_lost_uuid_dir_under_an_ignored_socket_dir_fails ... FAILED
        repair_upgrades_a_legacy_uuid_gitattributes ... FAILED
        repair_of_a_lost_uuid_dir_restores_its_gitignore_and_gitattributes ... FAILED
branch: 4 passed; 0 failed

Per-issue checklist for #1581:

  • Legacy * -text survives repair (healthy artifact, and an LFS
    pointer redownload) → repair_upgrades_a_legacy_uuid_gitattributes
    (also asserts git check-attr filter = unset under a repo
    *.tgz filter=lfs rule).
  • Lost uuid dir comes back without .gitignore/.gitattributes, so
    the tarball is ignored by *.tgz →
    repair_of_a_lost_uuid_dir_restores_its_gitignore_and_gitattributes
    (asserts git check-ignore is empty).
  • Unoverridable .socket/ rule reports success →
    repair_of_a_lost_uuid_dir_under_an_ignored_socket_dir_fails, which
    fails with vendor_artifact_gitignored on both the first and the
    second run.
  • Core units in redownload.rs:
    npm_redownload_restores_the_uuid_metadata,
    npm_uuid_metadata_upgrades_a_legacy_gitattributes,
    npm_commit_check_refuses_only_an_unoverridable_rule.

Local runs:

  • cargo fmt --all -- --check and
    cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test --workspace --all-features --no-fail-fast: everything
    passes except tests that can't pass in this sandbox. Those fail the same
    way independent of this diff:
    • chmod-based write-failure tests, because the sandbox runs as uid 0
      (repair_invariants::repair_cleanup_failure_is_reported_in_json_and_silent_modes,
      repair_render::repair_exits_zero_and_stays_quiet_when_lock_file_unremovable,
      in_process_redirect write-failure tests, core
      copy_tree/vlt_heal/pypi_* write-failure tests).
    • e2e_bun_lockb::binary_shared_bundled_record_hosted_pin_is_managed,
      which reaches the real patches-api.socket.dev through a blocked proxy.
  • No wrapper changes are needed: npm/, pypi/ and gem/ only
    dispatch to the binary.

/code-review high

The review found 8 candidates. Fixed here:

  • symlinked vendor dir
  • check only on the rewrite run
  • workspace-mirror continue skipping the metadata step
  • error wording
  • vlt dir probe

Not fixed:

Follow-ups

  • socket-patch.vendor.json (the informational marker) isn't restored
    when the uuid dir is lost. It is never a trust input.

https://claude.ai/code/session_01WMCn4ztc3DCiDDSBZDtjHd


Note

Medium Risk
Changes repair/redownload behavior for all npm vendored artifacts and git-ignore probing; mistakes could block legitimate repairs or miss uncommittable artifacts, but scope is localized to vendor repair paths with extensive tests.

Overview
socket-patch repair now restores .socket/vendor/npm/<uuid>/.gitignore and .gitattributes for all npm-family vendored entries (not only vlt), including when the tarball is healthy and after a redownload. Legacy pre-#1541 * -text .gitattributes is upgraded to the full * -text -filter -ident -working-tree-encoding form so repo rules like *.tgz filter=lfs or *.tgz in .gitignore do not leave repaired artifacts uncommittable.

After rewriting metadata (or redownloading), repair runs a git commitability check via new npm_artifact_commit_check, surfacing vendor_artifact_gitignored when a rule such as .socket/ cannot be overridden—matching vendor behavior instead of reporting success while the next commit drops the artifact. Core adds shared restore_npm_uuid_metadata (used by redownload and repair, skips symlinked vendor dirs); the vlt-only helper is removed.

Docs in CLI_CONTRACT.md and ecosystems.md note that repair can fix uuid-dir git metadata. New CLI and unit tests cover legacy attributes upgrade, lost uuid-dir recovery, and unoverridable ignore failures.

Reviewed by Cursor Bugbot for commit 4de77a3. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
`socket-patch repair` redownloaded a broken npm, pnpm, yarn or bun
vendored tarball but never rewrote the `<uuid>/.gitignore` and
`.gitattributes` beside it; only vlt and JVM entries got them back.
A project vendored before #1541 kept the old `* -text` attributes, so
a repository `*.tgz filter=lfs` rule put the repaired tarball back
into Git LFS, and a lost uuid dir came back without its `!*`
.gitignore, so a stock `*.tgz` ignore rule left the tarball out of
the commit. Both reported a successful repair while fresh clones
kept failing.

Repair now rewrites both files for every npm-family entry, on the
healthy path and after a redownload, then asks git whether it would
commit the artifact. A rule the uuid .gitignore cannot override
(such as `.socket/`) fails with `vendor_artifact_gitignored`, the
same refusal `vendor` gives.

Fixes #1581

Assisted-by: Claude Code:claude-opus-5-5
A rewritten npm uuid .gitignore or .gitattributes is not an
artifact rebuild, as the vlt repair contract already says, so
repair reports no rebuilt event for it.

Assisted-by: Claude Code:claude-opus-5-5
Repair now asks git whether it would commit a healthy npm-family
artifact on every run, not only when it rewrote the uuid metadata,
so a .socket/ ignore rule keeps failing until it is fixed. It also
restores the metadata before repairing bun workspace mirrors, skips
a vendor dir reached through a symlink, and probes a vlt package
dir as a directory.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 21, 2026 09:19
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 21, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review at 1d7c6ad.

Slack announcement not sent: no Slack send tool in this burn-down run.


Generated by Claude Code

@mikolalysenko

Mikola Lysenko (mikolalysenko) commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator Author

Final review brief for 1d7c6ad

What it does. socket-patch repair now rewrites .socket/vendor/npm/<uuid>/.gitignore and .gitattributes for every npm-family flavor (it used to do this only for vlt), on healthy entries and after a redownload. It then asks git whether it would commit the artifact, and fails with vendor_artifact_gitignored when a rule the uuid .gitignore can't override (such as .socket/) would still drop it. This fixes #1581: a lost uuid dir came back without its .gitignore, so a *.tgz rule ignored the redownloaded tarball, and a pre-#1541 * -text file kept LFS on it.

Risk: low. The change is confined to the repair path and the shared redownload restore(). The metadata write is idempotent and is skipped on --dry-run and for symlinked vendor dirs. The new git probe reuses the existing gitignore_probe that vendor already runs.

Look here

  • repair.rs:422-444:''`` metadata restore plus the commit check, now ahead of the healthy/workspace-copy branch (this replaces the vlt-only block)
  • repair.rs:740-744:'''``` the same check after a redownload
  • redownload.rs:367-436: restore_npm_uuid_metadata (all npm flavors, skips symlinked dirs) and npm_artifact_commit_check
  • in_process_vendor.rs:5189-5300: three CLI tests. The PR description says they fail on main.

Verified

  • Read the full diff. CHANGELOG.md is untouched, and the docs (CLI_CONTRACT.md, docs/ecosystems.md) match the code.
  • Ran cargo test -p socket-patch-core --lib -- redownload:: npm_dir:: on the head: 23 passed, including the 3 new unit tests.
  • CI is green on the head: 36 success, 50 skipped by path filters (ci-ok and clippy pass). Mergeable, 0 unresolved threads, and Bugbot found nothing on 1d7c6ad.

Changes I made: none.

Open questions (non-blocking)

  • When .socket/vendor/npm is a symlink (an unsupported layout that vendor refuses with vendor_dir_symlink_unsupported), the metadata rewrite is skipped but the commit check still runs. git check-ignore exits 128 there ("beyond a symbolic link"; I reproduced this), so every repair run on that layout adds a vendor_artifact_gitignore_unchecked warning. It's harmless and arguably informative. A one-line guard could follow up if you'd rather keep it quiet.
  • The commit check now runs one git check-ignore per npm entry on every non-dry-run repair. Repair isn't a hot path.

Auto-merge is armed: approving sends it straight to the merge queue.

Refreshed 2026-10-21 for head bc4674ba14: head changed only by a merge from main (bc4674b). Conflicts in vendor/npm_dir.rs, pypi.rs and redownload.rs were resolved keeping both sides: write_uuid_metadata returns this PR's bool, PyPI's wrapper maps it to (), and restore runs both the npm and the new PyPI uuid-metadata restore. CI green.


Generated by Claude Code

…uuid-metadata

# Conflicts:
#	crates/socket-patch-core/src/vendor/npm_dir.rs
#	crates/socket-patch-core/src/vendor/redownload.rs
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

git check-ignore cannot answer past a symlinked .socket/vendor/npm,
so every repair run on that layout warned
vendor_artifact_gitignore_unchecked. vendor already refuses the
layout, and repair already skips its metadata rewrite there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WMCn4ztc3DCiDDSBZDtjHd
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

e2e (ubuntu-latest, e2e_vendor_composer_build e2e_redirect_composer_build, 2) failed on 4de77a3 during runner setup: shivammathur/setup-php reported Could not setup PHP 8.2 before any test ran. This PR only touches the npm vendor repair path and doesn't change composer, so the failure isn't from this PR, and there's no fix to port. I'll re-run the failed job once when the rest of the run finishes.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final reviewer: disarmed auto-merge. The head moved from bc4674b (my last brief) to 4de77a3 with a non-merge commit ("Skip the commit check for a symlinked npm vendor dir"), so the brief no longer covers the code. I'll re-brief once the push is older than 45 minutes; a human approval still sends it straight to the queue after I re-arm.


Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 4de77a3. Configure here.

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review again (burn-down agent), re-confirmed on the new head.

  • Head: 4de77a31d8 ("Skip the commit check for a symlinked npm vendor dir"). It is mergeable with main.
  • CI: 34/34 checks are green on this head, and 19 were skipped by tier. The composer setup-php failure from earlier passed on its re-run.
  • Bugbot: reviewed 4de77a3 and found no issues.
  • Auto-merge is still off. The final reviewer disarmed it at 21:49 and will re-brief this head before re-arming it.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief for 4de77a3

What it does. socket-patch repair now rewrites .socket/vendor/npm/<uuid>/.gitignore and .gitattributes for every npm-family flavor (it used to do this only for vlt), both on healthy entries and after a redownload. It then asks git whether it would commit the artifact, and fails with vendor_artifact_gitignored when a rule the uuid .gitignore can't override (such as .socket/) would still drop it. This fixes #1581. The new commit since my last brief (4de77a3) also skips that git check when .socket/vendor/npm is a symlink. That was the non-blocking open question in the earlier brief.

Risk: low. The change stays inside the repair path and the shared redownload restore(). The metadata write is idempotent and is skipped on --dry-run and for symlinked vendor dirs. The new guard reuses path::vendor_dir_symlink, the same check restore_npm_uuid_metadata and vendor already use.

Look here

Verified

  • Read the new commit in full and re-checked the overall diff. CHANGELOG.md is untouched.
  • Ran cargo test -p socket-patch-core --lib -- redownload:: npm_dir:: on the head: 24 passed.
  • Removed the new guard locally: linked_vendor_dir_skips_uuid_metadata_and_commit_check then fails, so the test covers the change.
  • CI is green on the head (ci-ok and clippy pass). The PR is mergeable with 0 unresolved threads, and Bugbot found nothing on 4de77a3.

Changes I made: none.

Open questions: none.

Auto-merge is armed, so approving sends this PR straight to the merge queue.


Generated by Claude Code

Merged via the queue into main with commit a71fc2c Oct 22, 2026
100 of 102 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-repair-npm-uuid-metadata branch October 22, 2026 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

repair leaves a pre-#1541 root <uuid>/.gitattributes as * -text, so the repaired Bun tarball goes back into Git LFS on the next commit

3 participants