Skip to content

fix(scan): surface cross-mode state — hosted wiring retained, vendored ownership, berry vendored-entry refusal - #207

Open
Mikola Lysenko (mikolalysenko) wants to merge 2 commits into
mainfrom
fix/mode-conversion-visibility-warnings
Open

fix(scan): surface cross-mode state — hosted wiring retained, vendored ownership, berry vendored-entry refusal#207
Mikola Lysenko (mikolalysenko) wants to merge 2 commits into
mainfrom
fix/mode-conversion-visibility-warnings

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Lane ADE of the yarn mode-conversion campaign: three adversarially-verified SILENCE gaps, each pattern-fixed with additive warnings only (precedent: #133/#159/#164/#189/#201). No exit-code or status changes anywhere. CLI_CONTRACT.md updated for all three.

Root causes (file:line, pre-fix)

A — hosted→agent is a silent no-op (crates/socket-patch-cli/src/commands/scan/mod.rs, agent branch ~L1545 JSON + human path)
scan --mode agent over a live hosted redirect reported status: success / applied with zero warnings while yarn.lock stayed pinned to patch.socket.dev and .socket/vendor/redirect-state.json stayed live. The takeover machinery structurally cannot see it: classify_overlap_takeover (mod.rs:602) keys on purls present in both ledgers (hosted ∩ vendored), so hosted-only wiring never triggers it — and the agent path loaded the redirect ledger (mod.rs:1472) only for updates[] merging. No npm/yarn hosted revert exists, so the "conversion" can never complete without another mode run.

D — vendored→agent refusal invisible at the envelope (mod.rs:1564-1571 via partition_skipped_selected(..., "vendored"), vendor_flow.rs:503-549)
Agent-mode --json recorded vendor-owned skips only deep in apply.patches[] (skipped/vendored); no top-level signal, no stderr line — a JSON consumer had to dig per-patch to learn the mode did not change. (Human path already printed [skip] … (vendored …).)

E — berry rewriter misdiagnoses our own vendored entry (crates/socket-patch-core/src/patch/redirect/mod.rs:2039-2053)
The unsupported-protocol branch refused socket-patch's OWN file:./.socket/vendor/… wiring with a detail hardcoding (workspace:/patch:/portal:/link:)file: not even listed — and named no remedy.

Matrix evidence

Repro'd by the matrix sweep (cells convy1-hosted2agent, convy4-hosted-agent, edgey1-roundtrip; convy1/convy4-vendored2agent; convy4-vendored-hosted) and re-verified here against production on throwaway copies of the verify dirs:

  • A old binary: exit 0, status success, warnings: null, grep -c patch.socket.dev yarn.lock = 1 after "conversion". New: same exit/status, warnings[0].code = hosted_wiring_retained (JSON + stderr), state untouched.
  • D old: exit 0, warnings: null, skip only in apply.patches[]. New: same envelope + warnings[0].code = vendored_ownership_retained naming the purl and remove <purl> / scoped vendor --revert migration path; vendored wiring untouched.
  • E old: redirect_yarn_berry_unsupported_protocol claiming (workspace:/patch:/portal:/link:). New: redirect_yarn_berry_vendored_entry naming the retirement path (remove <purl> / scoped vendor --revert, then re-run scan --mode hosted); other protocols now get the generic code with the entry's ACTUAL protocol. Lock byte-identical, exit 0, both binaries.

Fix summary

  • New run-level warnings[] (top-level {code, detail}, additive, mirrored to stderr unless --silent) on the agent apply flow:
    • hosted_wiring_retained — new hosted_wiring_retained_purls probes the already-loaded redirect ledger, reusing hosted_wiring_live per scanned purl (deliberately NOT via classify_overlap_takeover, which requires a vendored-ledger intersection). Fires only when ledger records are still live at scan time AND the live lock proves hosted wiring — so lane B's hosted→vendored pre-revert (which retires the records) retires this warning with them, pinned by scan_agent_hosted_warning_silent_once_ledger_records_are_gone. Detail: stay --mode hosted or migrate via scan --mode vendored; never a hosted→agent unwind (none exists), never hand-deleting the ledger.
    • vendored_ownership_retained — fires when the vendored partition is non-empty, naming the purls + remove <purl> (or vendor --revert, blast radius called out), per-patch records untouched.
  • Berry rewriter: file: + .socket/vendor/ signature ⇒ distinct redirect_yarn_berry_vendored_entry code with the real remediation; otherwise the generic detail names the ACTUAL protocol(s). Refusal stays fail-closed/byte-identical.

Tests (TDD, RED commit 0842aca first)

  • scan_invariants.rs: 4 new e2e cases (wiremock, seeded ledgers — no real yarn needed): D warning trigger; A warning trigger; A silence when records are gone (lane-B coordination guard); A silence on a registry-clean lock.
  • scan/mod.rs unit tests beside the takeover suite: hosted_only_wiring_fires_agent_probe_not_the_overlap_classifier (pins the structural blindness), per-case silence guards, detail-wording assertions, distinct-code assertions.
  • core redirect/mod.rs: vendored-entry code+remediation; actual-protocol naming for portal: and non-vendored file:; existing yarn_layering incident guard 4d still green.
  • Gates: cargo test --workspace green, cargo clippy --workspace --all-features -- -D warnings clean, touched files rustfmt-clean.

Sibling-PR conflict notes (parallel lanes)

🤖 Generated with Claude Code


Note

Low Risk
Changes are additive warnings and clearer refusal diagnostics only—no exit/status semantics, lockfile writes, or apply behavior changes beyond documentation and messaging.

Overview
Agent-mode scan --apply / --sync can now emit additive top-level warnings[] on the JSON envelope (and mirror to stderr unless --silent) when another patch mode’s wiring stays in place. vendored_ownership_retained fires when vendor-owned packages were skipped before download—the existing per-patch skipped/vendored records are unchanged; the warning names purls and migration (remove <purl>, or scoped vendor --revert). hosted_wiring_retained fires when redirect ledger records are still live and the lock still proves hosted routing for scanned purls (via new hosted_wiring_retained_purls + hosted_wiring_live); detail steers toward staying hosted or scan --mode vendored, not hand-deleting redirect-state.json. Neither warning changes exit code or status.

Hosted yarn berry rewriter: file: ranges into .socket/vendor/ now get redirect_yarn_berry_vendored_entry with retirement guidance instead of the generic unsupported-protocol message; other non-npm: protocols still refuse with redirect_yarn_berry_unsupported_protocol but the detail names the actual protocol(s) (e.g. portal:, file:), not a fixed four-protocol list.

CLI_CONTRACT.md documents the agent-flow warning codes and berry refusal codes; unit and e2e tests cover triggers, silence cases (records gone, registry-clean lock, unscanned purl), and wording.

Reviewed by Cursor Bugbot for commit 779f206. Configure here.

… retained, berry vendored-entry refusal)

Failing-first tests for three adversarially-verified silence gaps in the
mode-conversion matrix (additive warnings only; no exit/status changes):

* A — scan --mode agent over a live hosted redirect reports success with
  zero hint that yarn.lock still pins the patch server and the redirect
  ledger stays live: scan_invariants pins a top-level
  hosted_wiring_retained run warning (+ two silence guards: records-gone
  = lane-B pre-revert world, and registry-clean lock).
* D — agent-mode apply buries vendor-owned skips deep in apply.patches[]:
  scan_invariants pins a top-level vendored_ownership_retained warning
  naming the purls and the real migration path.
* E — the berry hosted rewriter refuses our OWN vendored
  file:./.socket/vendor/ entry under the generic unsupported_protocol
  code whose detail hardcodes '(workspace:/patch:/portal:/link:)'
  (file: not even listed) and names no remedy: redirect unit tests pin
  a distinct redirect_yarn_berry_vendored_entry code + remediation, and
  the generic detail naming the ACTUAL protocol.

Currently failing (RED):
  core: yarn_berry_vendored_file_entry_refused_with_distinct_code_and_remediation,
        yarn_berry_unsupported_protocol_detail_names_actual_protocol
  cli:  scan_agent_over_live_hosted_wiring_surfaces_run_level_warning,
        scan_agent_over_vendored_purl_surfaces_run_level_warning

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d ownership, berry vendored-entry refusal

Three additive-warning fixes for the yarn mode-conversion matrix's silence
gaps (lane ADE). No exit-code or status changes anywhere — hosted refusals
set the precedent (exit 0 + warning).

A — hosted→agent silent no-op (scan/mod.rs agent branch):
  scan --mode agent over a live hosted redirect reported success/applied
  while yarn.lock stayed pinned to the patch server and
  .socket/vendor/redirect-state.json stayed live. The takeover machinery
  can never see this state: classify_overlap_takeover keys on purls in
  BOTH ledgers (hosted ∩ vendored), so hosted-ONLY wiring is structurally
  invisible (pinned by a new unit test). New hosted_wiring_retained_purls
  probes the already-loaded redirect ledger directly, reusing
  hosted_wiring_live per scanned purl, and emits a run-level
  hosted_wiring_retained warning (top-level warnings[] on the scan --json
  envelope + stderr; interactive path prints the same after apply). The
  probe keys on ledger RECORDS still live at scan time, so a sibling flow
  that pre-reverts hosted wiring (lane B) retires the warning with the
  records even while the append-only edits remain — pinned by tests. The
  detail offers the two real options (stay hosted / migrate via scan
  --mode vendored), never a hosted→agent unwind (none exists for
  npm/yarn) and never hand-deleting the ledger (the only revert data).

D — vendored→agent refusal invisible at the envelope (scan/mod.rs:1564ff):
  agent-mode --json buried the vendor-owned skips deep in apply.patches[]
  (skipped/vendored) with no top-level signal. One run-level
  vendored_ownership_retained warning now names the purls + the real
  migration path (remove <purl>, or vendor --revert which unwinds EVERY
  vendored package, then re-run), mirrored to stderr when not --silent.
  Per-patch records, exit code, and status are untouched.

E — berry rewriter misdiagnosis (core redirect/mod.rs:2039):
  the unsupported-protocol branch refused our OWN vendored
  file:./.socket/vendor/ entries with a detail hardcoding
  '(workspace:/patch:/portal:/link:)' — file: not even listed — and no
  remedy. A file: range carrying the .socket/vendor/ signature now gets
  the distinct redirect_yarn_berry_vendored_entry code whose detail names
  the retirement path (remove <purl> / scoped vendor --revert, then
  re-run scan --mode hosted — correct both before and after lane B, which
  covers the opposite direction); other protocols keep the generic code
  with the entry's ACTUAL protocol named. Refusal stays fail-closed and
  byte-identical.

CLI_CONTRACT.md documents all three (agent-flow run-level warnings
section, hosted rewriter refusal codes, vendored skip row/bullet).

Tests: RED-committed integration tests now green (scan_invariants ×4,
core rewriter ×2) plus new unit tests beside the takeover suite
(structural-blindness pin, per-case silence guards, detail wording).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@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.

Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.

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

Reviewed by Cursor Bugbot for commit 779f206. Configure here.

<purl>` for that package (or `socket-patch vendor --revert`, \
which unwinds EVERY vendored package), then re-run `scan --mode \
agent`."
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remove blast radius understated

Medium Severity

New remediation text recommends socket-patch remove for vendored→agent and berry vendored→hosted migration but only scopes vendor --revert. It omits that remove also deletes the package's vendor artifact dir, drops the .socket/manifest.json entry, restores recorded lock fragments, and sweeps unused blobs—side effects bots often run with --yes.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by learned rule: Remediation text must accurately state the full blast radius of recommended commands

Reviewed by Cursor Bugbot for commit 779f206. Configure here.

Do not delete `.socket/vendor/redirect-state.json` by hand: it \
holds the recorded pre-redirect lockfile originals (the only \
revert data) and the redirect records VEX reads."
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

False vendored ledger reconcile claim

Medium Severity

hosted_wiring_retained_detail tells users to migrate with scan --mode vendored and claims that run reconciles superseded redirect ledger entries. Today only cargo has that pre-revert; npm/yarn vendoring can rewrite the lock but leaves redirect-state.json records in place, so the advice overstates cleanup for the yarn conversion cases this PR targets.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 779f206. Configure here.

async fn load_ledger(root: &Path) -> Option<socket_patch_core::patch::redirect::RedirectState> {
socket_patch_core::patch::redirect::load_redirect_state(root)
.await
.unwrap()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

New tests use bare unwrap

Low Severity

New unit and e2e fixtures call bare .unwrap() on tempdir, filesystem, and JSON setup steps. On failure CI only reports a generic unwrap panic instead of which fixture step broke.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by learned rule: Prefer .expect("context") over bare .unwrap() in test code

Reviewed by Cursor Bugbot for commit 779f206. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant