Skip to content

Make the vendored-to-hosted takeover atomic - #1039

Merged
Mikola Lysenko (mikolalysenko) merged 22 commits into
mainfrom
arch-fix/takeover-atomic
Oct 8, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 22 commits into
mainfrom
arch-fix/takeover-atomic

Conversation

@mikolalysenko

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

Copy link
Copy Markdown
Collaborator

Problem

Architecture audit B03: scan/get --mode hosted over a vendored package (the vendored→hosted takeover) reverted the vendored wiring on disk first and only then planned the hosted pin. When the hosted rewriter refused (a lock-level refusal, a missing berry checksum, unavailable wheel metadata, a Poetry 0.x lock, a uv pin-down, ...), the package was left unpatched in both modes. Hand-copied pre-gates for bun, berry, classic, vlt, Gradle, pypi platform wheels and requirements tried to predict those refusals, and they drifted from the rewriters they copied (the root cause behind #945, #723 and the closed #699). The hosted→vendored direction is already atomic (#963).

  • B14: hosted writes were non-transactional. Files were written one by one, and an error exit after the takeover still said "nothing was written".
  • B37 (takeover part): the dry run counted every takeover as redirected without running the rewriter.
  • B15 (takeover list): the disk flow took over cargo/npm/golang/pypi/Gradle maven, but the in-memory engine refused only cargo/npm/golang.

Change

The takeover now runs inside the run's GroupCommit (commands/scan/hosted/takeover.rs):

  1. Each vendored revert is staged in the overlay under a savepoint. A revert that fails or keeps drifted wiring is rolled back and refused, as before.
  2. The hosted rewrite reads the overlay, so it plans against the reverted project.
  3. A staged purl the rewrite does not pin is retracted. The overlay goes back to its pre-revert state, the purl stays vendored byte for byte, the rest are staged again and the rewrite runs again. Each pass drops at least one purl, so the loop ends. The retracted purl is skipped with its cause (an existing skip reason, the rewriter warning that names it, redirect_requirements_takeover_unreachable, the rewriter's lock-level refusal, or redirect_takeover_not_pinned), followed by the new redirect_takeover_kept_vendored warning. Exit 0.
  4. The hosted pins and the vendored ledger go into the same overlay and are committed once (journaled). Reverted artifacts are deleted only after the commit (GroupCommit::defer_removals; the per-unit revert removal goes through remove_tree_and_prune, bun workspace tarballs through remove_mirror, and the JVM revert's Gradle/Maven tree files through group_commit::defer_removal). The JVM-owned .gitattributes files and the derived maven-metadata.xml files are now in the captured set, so the Gradle revert's writes stay in the overlay too.
  5. --dry-run runs the same steps and drops the overlay instead of committing, so it reports the wet outcome (B37).
  6. A hosted run with no takeover also stages its writes, but commits them unjournaled (commit_unjournaled) because hosted mode writes nothing under .socket/vendor/. If one replacement fails, the files already replaced are put back (B14). The few files the group does not capture (the Gradle hosted index and script under .socket/gradle/) are written just before the commit and put back if a later step or the commit fails.
  7. B15: hosted::takeover::in_reach is the one predicate both engines use.

Duplicate copies deleted

  • Takeover pre-gates that copied rewriter logic: 9 → 0 (bun text/binary, berry lock, berry dep, classic, vlt, Gradle takeover_refusal, pypi platform wheel, requirements reach). preflight_requirements_takeover only explains a retraction now. Review found the Gradle gate was not yet safe to remove, because the JVM revert deleted the vendored tree straight from disk. That is fixed in this PR (see below), so the count stands. preflight_yarn_classic_hosted is now private. After merging main's Fix yarn berry project gates drifting between modes (#628, #629) #657, which moved the berry project gates into the shared formats/yarn/berry_gates, the takeover again checks yarn-berry entries against those gates on the pre-revert project. It calls the rewriter's own preflight_yarn_berry_hosted once per staging pass, so no logic is copied. This is needed because the berry revert re-renders package.json in its majority line ending, so after the revert the rewriter could no longer see the mixed manifest that Hosted yarn berry rewrites a mixed-line-ending package.json that vendored mode refuses #628 refuses. Copied pre-gates: 9 → 0. Shared-gate call sites: 1.
  • Takeover-capable lists: 2 → 1 (stages.rs closure + hosted.rs closure → hosted::takeover).
  • Dry-run takeover preview path: the engine's TakeoverPreview install-policy preview and the CLI's withheld-and-counted preview are deleted. The dry run is the real rewrite now.
  • Stranded-takeover reporting (redirect_takeover_unpatched, partial_failure, unrecorded) is deleted, because a takeover can no longer strand.
  • The cargo/golang revert removal copies (remove_tree + prune_empty_vendor_levels) now use the shared remove_tree_and_prune.

Behavior changes (documented in CLI_CONTRACT.md)

  • A takeover the rewriter would not pin keeps the package vendored with exit 0. Before, it was stranded with exit 1.
  • A failed write or commit changes nothing and exits 1 with "nothing was changed". Before, the takeover and earlier locks were already on disk.
  • A dry run over a vendored pnpm project now previews trustLockfile as written to a new pnpm-workspace.yaml, which is what the wet run does: the revert removes the vendor-created file. The old preview said "merged into the existing" one.
  • With a yarn-offline-mirror, the yarn classic hosted rewriter no longer also warns redirect_yarn_classic_berry_migration_risk, because the mirror refused every pin and nothing was pinned. That warning came from Fix hosted yarn classic pins missing berry warning (#907) #917 on main. Without this fix, a retracted classic takeover reported the berry warning as its cause instead of redirect_yarn_classic_offline_mirror.
  • A vlt takeover over a BOM'd lock is refused by the staged revert itself (vendored_revert_failed) instead of the deleted vlt pre-gate. The state stays the same.

Testing

All commands were run in the worktree with CARGO_INCREMENTAL=0 through the shared limiter, -j4, on the branch after merging origin/main (431b818, head 830726e):

  • cargo test -p socket-patch-core --lib: 5608 passed, plus the new mirror test.

  • cargo test -p socket-patch-cli --lib: 869 passed.

  • cargo test --no-fail-fast -p socket-patch-cli with --test set to each of: coverage_fix_scan_hosted_dryrun_vendored, e2e_golang_hosted_state, covgap_commands_scan_hosted, in_process_redirect, hosted_memory_engine, mode_migration_pypi, in_process_vendor, in_process_vendor_bun_takeover, in_process_vendor_npm_v1_takeover, in_process_vendor_pnpm_takeover, in_process_vendor_pypi_takeover, vendor_eject, vendor_jvm_cli, contract_gradle_codes, gradle_agent_cli, e2e_sbt_vendor, e2e_scala_cli_vendor, covgap_commands_vendor, covgap_commands_vex, e2e_vex_redirect, e2e_redirect_gradle_build, e2e_vendor_gradle_build. All passed. The real-Gradle tests are #[ignore]d.

  • After the second merge and the berry gate: --lib, in_process_vendor (121, including Fix yarn berry project gates drifting between modes (#628, #629) #657's berry_takeovers_refuse_before_reverting_the_old_mode), mode_migration_npm (19, real yarn), mode_migration_pypi, mode_migration_cargo, mode_migration_bun, mode_migration_vlt, in_process_alternate_installers, remove, repair, e2e_redirect_gem_stale_install and the takeover files above all passed.

  • mode_migration_cargo::vendored_then_hosted_takeover_leaves_pure_hosted (real cargo) and mode_migration_npm::{berry,classic}_vendored_then_hosted_takeover_leaves_pure_hosted (real yarn): passed.

  • cargo clippy -p socket-patch-core -p socket-patch-cli --all-targets -- -D warnings: no findings in code this PR changed, but it fails on 20 pre-existing lints in files this PR does not touch, or in lines it did not write (checked with git blame against the merge-base):

    • python_crawler.rs:2760 unused unix_default, and :5375
    • patch/apply.rs:3736
    • jvm_jar.rs (8 × from_ref)
    • redirect/mod.rs:10292 (test)
    • upstream/uv.rs:2097
    • bun_binary.rs:1051
    • maven_repo.rs:2503
    • nuget_feed.rs:2005
    • pnpm_lock.rs:3877
    • yarn_berry_lock.rs:1483
    • yarn_layering_tests.rs:1023,1028
    • The CLI test helpers prebuilt_common/common (module loaded twice, needless borrows).

    With -A unused_variables so the core library compiles, the CLI src/ has no findings.

  • Not run locally: the real-Gradle leg e2e_redirect_gradle_build -- --ignored gradle_hosted_ (including gradle_hosted_takeover_refusal_keeps_vendored). This machine has no JDK, so it is left to the Gradle CI leg. The same staged revert is covered in-process by the new core test below.

New or flipped regression tests (each fails without its fix):

  • vendor::jvm::gradle::tests::a_staged_revert_changes_nothing_until_its_group_commits covers the review blocker. It vendors a Gradle patch, with and without a sibling version that shares maven-metadata.xml, then stages the real revert in a defer_removals group. Dropping the group (the dry run) or rolling it back (the retracted takeover) leaves the full tree byte-identical, and committing lands exactly the plain revert. Before the fix, the drop case deleted the jar, pom, marker, metadata and both .gitattributes.
  • Full-tree snapshots (.socket/ and the vendored artifacts included) now wrap the dry-run takeover in coverage_fix_scan_hosted_dryrun_vendored (pnpm, package-lock, vlt), e2e_golang_hosted_state::hosted_takeover_of_vendored_module_removes_vendored_state (golang, via get --dry-run) and mode_migration_cargo::vendored_then_hosted_takeover_leaves_pure_hosted (cargo). Bun (mode_migration_bun, assert_unchanged) and uv (uv_takeover_without_wheel_metadata_keeps_the_package_vendored, which includes the wheel) already compare the whole state.
  • patch::redirect::tests::yarn_classic_offline_mirror_refusal_skips_the_berry_risk_warning. It fixes in_process_vendor::classic_vendored_to_hosted_takeover_refuses_with_offline_mirror, which failed after merging main's Fix hosted yarn classic pins missing berry warning (#907) #917.
  • From the first round: mode_migration_pypi::uv_takeover_without_wheel_metadata_keeps_the_package_vendored, a_crash_inside_the_takeover_commit_is_finished_by_the_next_run, ledger_update_failure_changes_nothing, covgap_commands_scan_hosted::ledger_save_failure_after_successful_revert_fails_closed, in_process_redirect::partial_lockfile_write_failure_exits_1_and_writes_no_ledger, hosted_memory_engine::vendored_pypi_takeover_is_refused_like_the_disk_flow, group_commit::tests::deferred_unit_removals_wait_for_the_commit.

mode_migration_npm::berry_vendored_then_hosted_takeover_leaves_pure_hosted now expects a hosted resolutions entry in package.json instead of a pristine one. I checked it on origin/main (431b818, real yarn) in a detached worktree, and it already fails there at mode_migration_npm.rs:1223 ("the berry resolutions entry must be reverted"). Since #465, main's hosted berry pin writes resolutions into package.json. So the old assertion was stale, and this PR does not change that behavior.

Deferred

🤖 Generated with Claude Code


Note

Medium Risk
Changes core hosted-mode lockfile and vendor-ledger mutation paths and exit/status contracts; behavior is safer when redirects fail but regressions could affect multi-package takeovers or crash recovery.

Overview
Vendored → hosted takeover is now staged and committed atomically instead of reverting vendored wiring on disk before planning hosted pins. Hosted scan/get open a GroupCommit, run takeover logic in new hosted/takeover.rs, stage each revert under savepoints, plan the hosted rewrite against the overlay, and retract any package the rewriter would not pin (overlay rolled back; package stays vendored with redirect_takeover_kept_vendored, exit 0). Successful runs commit vendored reverts, lockfile redirects, and ledger updates in one step; artifact deletes are deferred until after commit.

Hosted writes are transactional: takeover runs use a journaled commit; hosted-only runs use commit_unjournaled with rollback on partial failure. --dry-run runs the same pipeline and drops the overlay, so previews match wet runs without touching disk.

Removed the stranded-takeover path (redirect_takeover_unpatched, partial_failure exit 1) and ecosystem-specific takeover pre-gates copied from rewriters; hosted::takeover::in_reach is shared with the in-memory engine. CLI_CONTRACT.md documents staged takeover, new skip/warning codes, and updated failure semantics (e.g. ledger save failure leaves the project unchanged).

Tests add full-tree dry-run snapshots and flip expectations for ledger/commit failures and partial lock writes.

Reviewed by Cursor Bugbot for commit 406e111. Configure here.


Generated by Claude Code

A group commit can now defer the vendored artifact deletions a revert
makes (GroupCommit::defer_removals): every per-unit revert removal goes
through remove_tree_and_prune (cargo and golang now too, instead of their
own remove_tree + prune copies) and the bun workspace tarball removal
through remove_mirror, and both queue the deletion for after the commit
when the open group asks for it. A rollback_to forgets the queued
deletions and a dropped group never makes them, so a staged revert can be
undone with its artifact intact.

Also:
- commit_unjournaled: the all-or-nothing replace without the crash
  journal, for runs that must write nothing under .socket/;
- a journal that had to create .socket/vendor/ prunes it again;
- group_commit::exists is public, for overlay-aware existence checks.

Audit B03/B14 groundwork.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The disk flow took over cargo, npm, golang, pypi and Gradle maven
entries, while the in-memory engine refused only cargo, npm and golang,
so a vendored PyPI package reached the Python rewriters in memory. Both
now use hosted::takeover::in_reach (audit B15).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
scan/get --mode hosted reverted a vendored package's wiring on disk
first and planned the hosted pin afterwards. When the rewriter then
refused (a lock-level refusal, a missing berry checksum, unavailable
wheel metadata, a Poetry 0.x lock, ...), the package was left unpatched
in both modes, and only six hand-copied per-ecosystem pre-gates tried to
predict those refusals. The dry run counted every takeover as redirected.

The takeover now runs inside the run's group commit:
- each vendored revert is staged in the overlay under a savepoint (a
  failing or drift-keeping revert is rolled back and refused);
- the hosted rewrite reads the overlay, so it plans against the
  reverted project;
- a staged purl the rewrite does not pin is retracted: the overlay goes
  back to its pre-revert state, the purl stays vendored byte for byte
  (redirect_takeover_kept_vendored, skipped with the cause), and the
  rest are staged and rewritten again;
- the hosted pins and the vendored ledger are written into the same
  overlay and committed once (journaled); artifacts go after the commit.
A dry run does the same and drops the overlay, so it reports the wet
outcome. A hosted run without a takeover commits its files unjournaled,
putting back the ones replaced if one fails.

Deleted: the bun, berry (lock and dep), classic, vlt, Gradle, pypi
platform-wheel and requirements pre-gates (9 copies of rewriter logic ->
0; the requirements reach check only explains a retraction now), the
dry-run TakeoverPreview path in the engine, and the stranded-takeover
reporting (redirect_takeover_unpatched), which can no longer happen.

Audit B03, B14, B37 (takeover part).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko Mikola Lysenko (mikolalysenko) added the arch-refactor PR opened by the scheduled architecture refactor routine label Oct 7, 2026
The staged vendored-to-hosted takeover runs the real revert inside a
group commit and relies on the overlay plus deferred removals to undo
it. The JVM revert deleted the tree files under .socket/vendor/gradle
and .socket/vendor/maven2 directly, and wrote or deleted the owned
.socket/gradle/.gitattributes, .socket/vendor/.gitattributes and the
derived maven-metadata.xml files straight to disk. A dry run, or a
takeover the hosted Gradle planner refused and retracted, therefore
deleted the vendored jars while the restored wiring still named them.

Capture the owned .gitattributes files and the derived metadata in the
group overlay, and route the tree-file deletions through
group_commit::defer_removal so they happen only after the commit. A
new test stages the revert (with and without a sibling version sharing
the metadata) and checks that dropping or rolling back the group leaves
the project byte-identical and that committing lands the plain revert.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
commit_hosted_writes writes the files the group does not capture (the
Gradle hosted index and script under .socket/gradle/) straight to disk
before the commit. When writing a later file, saving the vendored
ledger or the commit itself failed, the error said nothing was changed
while those files stayed on disk. Record their previous bytes and put
them back on every failure path except an interrupted journaled
commit, which the next locked command finishes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Snapshot every project file, .socket/ and the vendored artifacts
included, around the dry-run vendored-to-hosted takeover for pnpm,
package-lock, vlt, golang and cargo (bun and the uv retract test
already compare the artifact). Rewrite the stale CLI_CONTRACT Gradle
paragraph that still described the deleted takeover_refusal pre-gate,
and the real-Gradle refusal test's doc comment.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The takeover pre-gates that called preflight_yarn_classic_hosted and
preflight_yarn_berry_hosted from outside are gone. The classic one is
now private and the berry one pub(crate) (upstream/npm.rs still uses
it).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With a yarn-offline-mirror configured the classic hosted rewriter
refuses every entry, so nothing is pinned, yet it still warned that a
berry install would drop the hosted pins (#907's warning counted the
refused entries as pinned). The staged takeover reports a retracted
purl's first rewrite warning as its cause, so a vendored classic
project with a mirror was skipped as redirect_yarn_classic_berry_
migration_risk and the real refusal, redirect_yarn_classic_offline_
mirror, was never reported (in_process_vendor's
classic_vendored_to_hosted_takeover_refuses_with_offline_mirror failed
once main's #917 landed beside the staged takeover).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Resolves the conflicts with #657: the berry takeover pre-gate it
extended stays deleted in hosted.rs, preflight_yarn_berry_hosted takes
main's manifest argument, preflight_yarn_berry_hosted_dep stays
deleted (its only caller was the deleted pre-gate), and the contract
keeps main's root package.json line-ending sentence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#657 (merged on main) made the hosted and vendored modes refuse a mixed
root package.json, and gated the vendored-to-hosted takeover before its
revert. The staged takeover dropped that pre-gate and let the hosted
rewriter judge the reverted project, but the berry revert re-renders
package.json in its majority line ending, so a mixed manifest passed
the rewriter's check after the revert and the takeover went ahead
(in_process_vendor berry_takeovers_refuse_before_reverting_the_old_mode
failed after the merge).

Judge the berry project gates once per staging pass, on the pre-revert
overlay, through the rewriter's own preflight_yarn_berry_hosted (the
shared berry_gates set, no copied logic). A refused yarn-berry entry is
skipped with the gate's code, followed by redirect_takeover_kept_vendored.
preflight_yarn_berry_hosted is public again for this caller.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 7, 2026 17:07
@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.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-cli/src/commands/scan/hosted/takeover.rs
Comment thread crates/socket-patch-cli/src/commands/scan/hosted/takeover.rs
…asons

When a staged takeover is retracted and no rewriter warning names the
package, explain() fell back to the rewrite's first warning as the
lock-level cause. That warning can be a success advisory from a pin that
did land (redirect_npm_allow_remote, redirect_pnpm_trust_lockfile,
redirect_yarn_classic_berry_migration_risk), so the skipped purl and
redirect_takeover_kept_vendored reported the wrong code. Skip those
advisories when picking the fallback; with nothing else left the reason
is NOT_PINNED.

names_package accepted `/` as a left boundary unconditionally, so an
unscoped name like `node` matched inside `@types/node` and a retracted
takeover could inherit another package's warning. A `/` now counts as a
boundary only after a path segment, not after an `@scope`.

Co-Authored-By: Claude <noreply@anthropic.com>
@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 406e111. Configure here.

Resolve conflicts with main's #946 (PyPI takeover pre-gate), #1042
(containment helper) and #1077 (classic berry-migration warning):

- hosted.rs: keep this PR's staged takeover; main's new
  `preflight_pypi_takeover` pre-gate inside the deleted
  `vendored_takeover` is dropped. The staged takeover's `explain` now
  calls `preflight_pypi_takeover` (instead of only the requirements
  check), so a retracted uv pin-down (#723) or Poetry 0.x (#945)
  takeover is still skipped with `redirect_uv_takeover_version_unreachable`
  / `redirect_poetry_lock_unsupported`, as main's tests expect.
- socket_dir.rs: use main's `containment::ensure_unlinked` guard, then
  this PR's deferred removal.
- redirect/mod.rs: take main's `pinned_any` (a mirror-refused entry
  counts only when it already carries our hosted pin), which subsumes
  this PR's mirror fix.
- CLI_CONTRACT.md: describe the uv/Poetry cases as retractions.
- mode_migration_pypi.rs: keep both sides' tests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Oct 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a manual request Oct 8, 2026
@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Oct 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 8, 2026
Resolve CLI_CONTRACT.md: keep this PR's staged-takeover wording and add
#1060's redirect_gem_version_not_locked code.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The required 'Audit GHA Workflows' check (zizmor ref-version-mismatch)
now fails on every head because the pinned setup-php hash no longer
matches the moving v2 tag. Same one-line change as #1118, so it merges
cleanly when that lands.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Oct 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 8, 2026
Resolve the hosted.rs conflict with #1045: this branch moved the
takeover out of hosted.rs into hosted/takeover.rs, so keep that layout
and port #1045's change there. The takeover's ledger drop now compares
PurlKeys and the local canonical_purl key helper is gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Resolve CLI_CONTRACT.md: keep this PR's staged-takeover wording and
add main's new gem redirect codes (bundle lockfile, twin manifest).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Adopt #1032's jvm::layout module: group_commit captures through
layout::CAPTURED_FILES (now also listing the Gradle script and vendor
.gitattributes this PR captures) plus the derived maven-metadata paths;
drop this PR's duplicate VENDOR_TREES for layout::VENDOR_TREES; the
takeover reach check uses layout::LEDGER_ECOSYSTEM.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikolalysenko
Mikola Lysenko (mikolalysenko) added this pull request to the merge queue Oct 8, 2026
Merged via the queue into main with commit 823810a Oct 8, 2026
558 of 561 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the arch-fix/takeover-atomic branch October 8, 2026 14:55
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 8, 2026
scan/hosted.rs: add main's takeover module (#1039) and keep this branch's
vlt_heal alias in place of scan/hosted/vlt.rs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 8, 2026
Resolve two CLI_CONTRACT.md conflicts by keeping main's new text (the
atomic vendored-to-hosted takeover wording from #1039 and the
gem_lock_unsupported warning from #768) while re-applying this PR's
migration away from the removed spellings: `scan --apply` becomes
`scan --mode agent`, and `--apply`/`--vendor` in the lockfile
supplement become agent mode / vendored mode.

Co-Authored-By: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 8, 2026
Main's #1039 made the vendored-to-hosted takeover staged in the run's
group commit (reverts live in an overlay until one commit, an unpinned
takeover is retracted and stays vendored, dry runs stage too). The PR's
attribution gate is re-applied on that structure:

- RewriteOptions::takeover_uuids is the staged takeovers' uuids
  (Takeover::staged_uuids), in wet and dry runs alike: the rewriters'
  verdict decides them and main's retract loop handles an unpinned one.
- Scan's prior discovery is reused only when no takeover is staged: its
  re-stat sees the disk, not the overlay holding the reverts.
- done.unattributed joins `skipped` after the retract loop, from the
  final rewrite.
- The writes go through main's commit_hosted_writes; the paths created
  for discovery_after_writes are taken before the commit (written paths
  not yet a regular file on disk, plus takeover files the overlay
  creates or removes), and a takeover's reverts count as writes when
  choosing the post-write discovery.
- engine::rewrite drops the TakeoverPreview argument main deleted;
  docs and CLI_CONTRACT describe the staged takeover.

Co-Authored-By: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 8, 2026
Picks up the atomic vendored-to-hosted takeover (#1039); merges cleanly
on top of the earlier main merge.

Co-Authored-By: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 8, 2026
main's #1039 (atomic vendored-to-hosted takeover) dropped the
takeover_previews parameter from hosted::engine::rewrite. The merge
itself is clean, but this branch's bun.lockb rewrite test still passed
`&[]` for it, so the merge queue failed to compile socket-patch-core's
tests. Drop the stale argument.

Assisted-by: Claude Code:claude-opus-5-5

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TSx4W4Qfb8hsBhw4NEvkv9
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 8, 2026
Main's #1039 made the vendored-to-hosted takeover atomic: it moved the
takeover into scan/hosted/takeover.rs and dropped dry-run takeover
previews. Take main's hosted.rs; the branch's pnpm gitBranchLockfile
takeover gate is now covered by the staged takeover's retract path,
since the hosted rewrite emits redirect_pnpm_git_branch_lockfile and
leaves the purl vendored. Keep the branch's per-member and Rush lock
arguments to pnpm_trust and its governing-lock gate, minus the
takeover_previews plumbing main removed.

Co-Authored-By: Claude <noreply@anthropic.com>
This was referenced Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-refactor PR opened by the scheduled architecture refactor routine

Projects

None yet

3 participants