GeezerVison is showing github.com — vendor --check and vex misreport a crashed vendored run whose commit journal is pending, because only lock-taking commands replay it · Issue #809 · SocketDev/socket-patch
vendor --check and vex misreport a crashed vendored run whose commit journal is pending, because only lock-taking commands replay it #809Description[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: discussion #560 register.
Kind: bug. Source: new finding; register row C46 (related to C26, #808).
Problem
A vendored run that crashes after its commit journal is durable has committed: the contract says the next command that takes apply.lock replays .socket/vendor/.commit-journal.json before reading anything. The replay lives only inside the lock (apply_lock::acquire). The commands that read vendored state without the lock (vendor --check, vex, list, apply --check) never look for a pending journal, so they diagnose the half-written tree as if it were final:
vendor --check ("no API client, lock recovery, …") fails every patch with vendor_ledger_missing, exit 1. That code's documented recovery is "restore .socket/vendor/state.json from version control … or git checkout -- <lockfile> and re-vendor". The right remedy is to run any locked command, and nothing says so.
- pnpm
vex omits every vendored package ("the patched files still hold the original content (not_applied)") and exits 1.
The contract allows vex and list to "observe the interrupted state", but not vendor --check, the CI gate. None of the readers mentions that a commit is pending.
Proof (debug build, vendor_ecosystem_fixtures, run twice on 045d7ec, both runs identical). vendor --json with SOCKET_PATCH_FAILPOINT=group_commit_file@1 (exit 86; journal present), then:
| fixture |
command |
after the crash |
after one locked vendor --dry-run replays the journal |
uninterrupted run |
| npm |
vendor --check --json |
exit 1, 2 × vendor_ledger_missing |
exit 0, 2 × vendor_check_ok |
exit 0, 2 × vendor_check_ok |
| pnpm |
vendor --check --json |
exit 1, 2 × vendor_ledger_missing |
exit 0, 2 × vendor_check_ok |
exit 0, 2 × vendor_check_ok |
| pnpm |
vex --offline |
exit 1, both packages omitted as not_applied |
exit 0, not_affected |
exit 0, not_affected |
| npm |
vex --offline |
exit 0, not_affected |
exit 0, not_affected |
exit 0, not_affected |
apply --check --json --offline exits 0 and leaves the journal in place, so it takes no lock either.
Symptoms: none filed. Impact: low frequency (a crash or SIGKILL inside the commit window), but the CI gate fails with a misleading code and a remedy that has users editing version-controlled state by hand.
Proposed change
Add one core probe, group_commit::pending(project_root) -> bool (the journal exists), and use it in every lock-free reader of vendored state:
- Preferred: when a journal is pending, take
apply.lock (which replays it), then read. Readers stay lock-free, and leave no residue, in the normal case.
- Alternative: fail
vendor --check with a new vendor_commit_pending code and make vex/list warn "an interrupted vendored commit is pending; run socket-patch repair".
Either way, delete the claim in the contract that lock-free readers "may observe the interrupted state", or narrow it to the alternative's warning.
Size and scope
utils/group_commit.rs (+~10), commands/vendor.rs run_check, commands/vex.rs, commands/list.rs, commands/apply.rs check path (~15 each), CLI_CONTRACT.md. About 80 production lines. Out of scope: the lock lifecycle (#808) and moving the replay out of the lock primitive (also #808).
Acceptance criteria
- A new case in
tests/vendor_group_commit_e2e.rs: after group_commit_file@1, vendor --check --json reports vendor_check_ok (preferred) or vendor_commit_pending (alternative), never vendor_ledger_missing.
- The same case for pnpm
vex: the statement matches an uninterrupted run (preferred) or stderr names the pending commit (alternative).
- With no journal,
vendor --check, vex and list still create no .socket/apply.lock and no .socket/.
-
vendor_group_commit_e2e and the vendor --check tests stay green.
Dependencies
None. If #808 introduces a project-session type, the readers should open it instead of the lock directly. Activitymikolalysenko commented on Oct 4, 2026 CollaboratorAuthor More actions [agent] Triaged as priority:p3: this is cross-cutting vendored crash recovery in the CLI core, not specific to one ecosystem. It is not a duplicate. It is related to #808 (the apply.lock lifecycle decision, which also proposes moving journal replay out of the lock primitive) but has a different cause: the lock-free readers (vendor --check, vex, list, apply --check) never probe for a pending .socket/vendor/.commit-journal.json. No open PR addresses it.
Generated by Claude Code mikolalysenko commented on Oct 9, 2026 CollaboratorAuthor More actions [agent] Reproduced with a real npm project on main a80b89e (Linux, npm 10.9.4), by the scheduled npm bug-hunt routine (ledger #302).
Setup: a dual-lock project (npm-shrinkwrap.json + package-lock.json) depending on [email protected] and [email protected]. The run was scan --mode vendored against a local patch-API mock. I killed it with SIGKILL from an LD_PRELOAD shim on rename() at one of two points: the package-lock.json rename, or the state.json rename. Both are after .socket/vendor/.commit-journal.json is durable.
Before any command that takes the lock:
vendor --check exits 1. For each package it reports vendor_ledger_missing: … the vendor ledger (.socket/vendor/state.json) has no entry for it; restore state.json from version control. On a first vendoring, state.json was never in version control, so that remedy can't be followed. The message doesn't mention the pending journal either.
vex exits 0 and attests both packages (2 statements). list prints "No patches in this project".
- At the
package-lock.json kill point the locks are split: the shrinkwrap is vendored and the package-lock is still on the registry.
socket-patch repair replays the journal. Both locks then come out vendored, state.json is written, and vendor --check passes. So the remedy that actually works is any command that takes the lock, not a VCS restore.
One more residue: the kill leaves a .socket-stage-package-lock.json-<uuid> (or .socket/vendor/.socket-stage-state.json-<uuid>) sibling in place. Neither the replay nor later runs remove it, so a git add -A would commit it.
Generated by Claude Code mikolalysenko commented on Oct 9, 2026 CollaboratorAuthor More actions [agent] Yarn Berry data point from the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305), main a80b89e, yarn 4.18.1, node-modules. rollback takes the lock, but it doesn't replay a pending journal either. Its ledger-missing check runs before acquire_or_emit.
The setup is a first scan --mode vendored (left-pad + uuid), SIGKILLed by an LD_PRELOAD rename() shim at the yarn.lock rename or at the state.json rename. The journal is durable in both cases. The next rollback exits 1 with Lockfiles still reference .socket/vendor/ artifacts but the vendor ledger is missing — restore .socket/vendor/state.json from version control …. The journal is still pending afterwards, so rollback can never succeed until some other lock-taking command (repair, vendor) replays it. At the yarn.lock kill point the pair is split: package.json holds the vendored file: resolutions and yarn.lock is on the registry. I reproduced it twice at each kill point. The same pre-lock ordering is behind #1241 (see my comment there for the takeover directions).
Generated by Claude Code mikolalysenko commented on Oct 9, 2026 CollaboratorAuthor More actions
v5 triage: closing. Closing as not planned under the requested v5 scope: this needs a process interrupted in the commit-journal window and then a read-only command. We assume a single normally completing CLI instance. Existing write/recovery behavior is not being removed; no additional reader recovery protocol is required for v5.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|