Repository navigation
fix(cli): activation leaves no permanent drift - #773
Merged
Merged
Conversation
`MarketplaceSyncSettingsUseCase` wrote `.claude/settings.json`, hashed what it wrote, saved the manifest, and only then shelled out to `claude plugin marketplace add` and `claude plugin enable` — which write into that same file. Claude Code declares no separate `enabledPluginsSettingsPath`, so both halves land there, and the file is hash-tracked precisely because the two paths coincide. Nothing re-read it. The tracked hash described content that stopped existing the moment activation succeeded: `status` and `doctor` reported a file the person never touched as drifted for as long as the manifest stood, and `restore` would have undone the host's own registration to reach a state AIDD held for the length of one function. The first probe of this passed, and was vacuous — the fake activator wrote the same key `mergeEnabledPlugins` already writes, so the content was byte-identical and the hash matched by coincidence. Writing a key this code never writes, which is what the host's own bookkeeping is, turned it red. After activation the settings file is re-read and what is there is stored, for the tools whose own CLI actually ran. `activateTool` now answers whether it drove the CLI. Only those tools: a tool whose binary is absent wrote nothing, so a file that differs for it differs because a person changed it — the drift `status` exists to report — and re-hashing every installed tool would bless that as ours and make the change permanent and invisible. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp
blafourcade
force-pushed
the
fix/activation-does-not-leave-permanent-drift
branch
from
September 4, 2026 23:32
17d64ee to
5d76a5c
Compare
blafourcade
deleted the
fix/activation-does-not-leave-permanent-drift
branch
September 6, 2026 06:22
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
MarketplaceSyncSettingsUseCase.executeran in this order:syncToolwrites.claude/settings.jsonand hashes what it wrote into the manifest.manifestRepo.save(manifest).activateNativeToolsshells out toclaude plugin marketplace addandclaude plugin enable.Step 3 writes into the same file step 1 hashed. Claude Code declares
settingsPath: ".claude/settings.json"and noenabledPluginsSettingsPath, so both halves land there — and the file is hash-tracked precisely because the two paths coincide (syncEnabledPluginsFile's ownif (settings.enabledPluginsSettingsPath == null)).Nothing re-read it. The tracked hash described content that stopped existing the moment activation succeeded, so:
statusanddoctorreported a file the person never touched as drifted, for as long as the manifest stood.restorewould have undone the host's own registration to reach a state AIDD held for the length of one function.The wrong answer that came first
The first probe passed — and was vacuous. The fake activator wrote
enabledPlugins: { <ref>: true }, which is exactly the keymergeEnabledPluginshad already written, so the content was byte-identical and the hash matched by coincidence. Replacing it with a key this code never writes — the host's own bookkeeping — turned the probe red.Change
After activation the settings file is re-read and what is there is stored, for the tools whose own CLI actually ran.
activateToolnow answers whether it drove the CLI.Only those tools. A tool whose binary is absent wrote nothing, so a settings file that differs from its tracked hash differs because a person changed it — the drift
statusexists to report andrestoreexists to undo. Re-hashing every installed tool would bless that as ours and make the person's change permanent and invisible.Re-read rather than re-derived: what is stored is what is on disk after the write — an observation, not a guess at what the host would have written.
Guards
The second is what keeps the first honest: without it the fix is indistinguishable from "always trust the file".
Windows caught the first push
Both new tests failed on
cli / Windowsat17d64ee7— a real failure, not the reporter flake #766 fixed. They built the settings key as a/-joined literal while the production code usesresolve, so on Windows the test addressed\\test-project\\.claude\\settings.jsonunder a name the use case never wrote. Now resolved the same way the code does, with the reason stated where the helper lives.Worth recording: the job that caught it is the one #766 existed to make trustworthy, and this is the first time since that it has failed for a reason that was actually mine.
Gates: 3454 tests / 308 files,
tsc,biome ci(2 pre-existing warnings, 0 errors), knip, jscpd, bundle within budget, 369 repository script tests, 0 broken links, cli layering clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01VWNxk63AGKkqE8HRqHLjGp