Skip to content

feat(adopt): consented user-scope migration plus rollback fidelity - #105

Merged
arcaven merged 1 commit into
mainfrom
feat/adopt-user-scope-migration
Aug 1, 2026
Merged

feat(adopt): consented user-scope migration plus rollback fidelity#105
arcaven merged 1 commit into
mainfrom
feat/adopt-user-scope-migration

Conversation

@arcaven

@arcaven arcaven commented Aug 1, 2026

Copy link
Copy Markdown
Member

A consumer whose vsdd-factory plugin is enabled machine-wide currently cannot adopt any repo: adopt refuses the user-scope enable (correctly, since converting one repo would leave every other repo on the machine in the posture the containment mandate grades an error), and the refusal pointed at hand-editing settings files with nothing checking the result. This closes the last gap in the conversion toolkit for that consumer, and fixes a rollback defect that shape exposes.

What changed

sideshow adopt <pack> --migrate-user-scope moves the machine-wide enable to per-repo enables:

  1. Pin the foreign channel in each swept repo that DEPENDS on the machine-wide enable. A repo enabled independently gets no write; a repo that suppresses the identity gets no write. The dependency is computed against a counterfactual census (the machine with the entry gone), not inferred from install records.
  2. Remove the machine-wide entry.
  3. Verify against disk, per repo, that removing it changed nothing. Any mismatch, or a factory run in flight in any swept repo, rolls the whole migration back through an undo log that restores prior values exactly.

The sweep set is disclosed rather than guessed: adoption target, sideshow ledger repos, harness-recorded project-scope installs, plus --also-repo <path> and --sweep-root <dir>. The plan prints the limit plainly, because sideshow cannot enumerate every repo on a machine and only the operator can finish that list. Consent is --yes after a printed plan, never an unanswered prompt (sideshow#94). Per-repo enables land in settings.local.json; --scope project writes the tracked file and also requires --commit-consent.

Rollback fix. A repo already carrying a per-repo enable has a true in the very key adopt's suppression overwrites, and rollback deleted the key instead of restoring the value, switching the foreign channel off in a repo nobody converted. That shape predates this branch (any hand-written local enable had it) and the migration produces it by design, so it is fixed here. The created-file removal path now also calls the preserve floor.

Two smaller pieces: adopt --finish decides the marketplace-removal question from the install registry ("serves only vsdd-factory, so removal is safe" with the command, or "KEEP" naming what else it serves) instead of deferring it, and the dry-run write validator creates nothing, not even the parent directory a real write would make.

The executing half of --finish (uninstall per scope, cache purge, marketplace removal) stays print-only: it is gated behind aae-orc-d3nq.20, and no removal path for a store version exists yet.

Tests

go test ./... green, golangci-lint run ./... reports 0 issues, gofmt clean. Test functions 270 to 289; cases including subtests 329 to 365.

The rollback regression test was verified failing against the pre-fix code (repo not restored after rollback: [.claude/settings.local.json], prior local enable not restored: map[]) before the fix landed. The dry-run test caught a real bug during development: the write validator was creating .claude/ directories, so a dry run wrote. Coverage includes the pin/no-pin decision across all four repo shapes, adopt succeeding on the target after migration, dry run writing nothing anywhere, four refusals (no machine-wide enable, project scope without commit consent, bad scope, factory lock in a swept repo that is not the target), the unwritable-settings prediction, project-scope pinning, the undo log, the sweep-root scanner, and the marketplace verdict both ways.

Cost of merge

Additive. The one behavior change to an existing path is the rollback restoring a prior enable instead of deleting it, which is strictly closer to "repo exactly as found."

Adopt refuses to convert a repo whose foreign channel comes from a
machine-wide (user-scope) enable, and the refusal had no answer in the
tool: the operator had to hand-edit settings in every repo that
depended on that enable, with nothing checking the result.

'sideshow adopt <pack> --migrate-user-scope' is the consented path out.
It pins the foreign channel in each swept repo that DEPENDS on the
machine-wide enable, removes the machine-wide entry, then verifies
against disk, per repo, that removing it changed nothing. A repo
enabled independently gets no write; a repo that suppresses the
identity gets no write. Any mismatch, or a factory run in flight in any
swept repo, rolls the whole migration back.

The sweep set is disclosed rather than guessed: adoption target,
sideshow ledger repos, harness-recorded project-scope installs, plus
--also-repo and --sweep-root. The plan states the limit plainly,
because sideshow cannot enumerate every repo on a machine. Per-repo
enables land in settings.local.json by default; project scope also
requires --commit-consent.

Also fixes a rollback defect this shape exposes. A repo already
carrying a per-repo enable has a true in the very key adopt's
suppression overwrites, and rollback deleted the key rather than
restoring the value, switching the foreign channel off in a repo nobody
converted. The regression test was verified failing against the prior
code. The created-file removal path now calls the preserve floor.

Two smaller pieces: adopt --finish decides the marketplace question
from the install registry instead of deferring it to the operator, and
the dry-run write validator creates nothing, not even the parent
directory a real write would make.

Refs: aae-orc-d3nq.22
@arcaven
arcaven merged commit b607548 into main Aug 1, 2026
10 checks passed
@arcaven
arcaven deleted the feat/adopt-user-scope-migration branch August 1, 2026 19:32
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