fix(installer): only rewrite the skills store the updated install owns - #657
Open
filip131311 wants to merge 1 commit into
Open
fix(installer): only rewrite the skills store the updated install owns#657filip131311 wants to merge 1 commit into
filip131311 wants to merge 1 commit into
Conversation
`argent update --local` re-synced the machine-wide skills store as well as the project's. `refreshArgentSkills` took no scope and acted on every store that tracked an argent skill, so a project-scoped update rewrote the skills every other project on the machine uses — pinning them, via the running package's own version, to a version the global binary is not. A store belongs to exactly one install: the global lock to the binary on PATH, a project's lock to whichever install serves that project. Callers now say which stores their update owns. The scope is required rather than defaulted, so a call site that forgets one is a compile error instead of a silent write to a shared store. This also closes the mirror case — a global update no longer rewrites a project's committed lock when that project runs a local install. Note the project scope is not "the local install's scope": a non-interactive global install writes a project lock too, and that lock tracks the global binary. The install mode is what tells them apart. The more damaging half was the prune. Obsolete skills are the difference against the running package's bundled set, and the removal carried `-g` for the global store — so an older install would classify everything added since as orphaned and delete it machine-wide. Between v0.15.0 and now that is two real skills. Pruning a store is now allowed only when it was last written by a version no newer than ours; an unreadable or unversioned lock means we cannot tell, so we leave it alone. Syncing stays unconditional — it is additive and recoverable, deletion is not. A store left untouched is now named in the output, so it does not just go quiet. Fixes #637
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.
Fixes #637. See my triage comment there
for the full findings — this PR fixes two defects, and the reported one is the milder.
1. The reported bug
refreshArgentSkills(projectRoot)took no scope and looped both specs, soargent update --localrewrote the machine-wide store. Reproduced against the shipped code with a sandboxed
HOMEand astub
npx:Callers now name the stores their update owns, and the scope is required — a call site that
forgets one is a compile error rather than a silent write to a shared store. (Both existing call
sites did forget; the compiler caught them.)
This also closes the mirror case:
argent update --globalno longer rewrites a project'scommitted
skills-lock.jsonwhen that project runs a local install.One trap worth naming: "is there a local install" is the wrong predicate.
chooseScopereturnslocalfor non-interactive global-mode installs too, andinit-skills.tsonly passes-gforan explicitly global scope — so a project lock can legitimately track the global binary. The rule
keys on
resolveInstallMode.2. The worse bug the fix had to also close
Narrowing scopes alone would have closed this issue while leaving a deletion path open.
Obsolete skills are computed as the difference against the running package's bundled set, and the
global spec's removal carries
-g. So an install older than the one that filled a store classifieseverything added since as orphaned and deletes it machine-wide. The skew is real —
v0.15.0ships 13skills, current ships 15. Reproduced:
And scope-narrowing does not stop it: any run whose targets include
globalstill prunes from therunning package's set — including plain
argent update --yeson a machine with both installs, and thetool-server's own
update-argent, which pins--globalwhile being served by the local install.So pruning a store is now gated on ownership: allowed only when the store was last written by a
version no newer than ours (the lock records a
refper entry). An unreadable or unversioned lockmeans we cannot tell, so we leave it alone. Syncing stays unconditional — it is additive and
recoverable; deletion is not.
Deliberate behaviour changes
binary owns that store — but it is a change, so the command now says so once rather than letting it
drift silently: "Left the machine-wide skills alone — they track the global install."
scope_countlegitimately drops from 2 to 1 for local updates. The field still means"scopes this refresh acted on", so I left the event schema alone.
Repair for anyone already affected
A re-pinned global store self-heals on the next run that includes the global target and is
executed by a package at least as new as the store's tracked set — i.e.
argent update --globalfromthe global binary. It does not heal when run by the older local install, which is what caused it.
Worth a line in the release note.
Verification
The original sandboxed repro, re-run against the fix:
and the deletion path, against a store written by a newer install:
installer suite 548 passed. Existing scope tests were passing partly by accident — they touched one
store only because the other lock happened not to exist — so each now states the scope it exercises,
and the new containment tests stage both stores so acting on the wrong one is visible.
Not in this PR
buildArgentSkillsSource(getInstalledVersion())uses the running package,so
argent update --localinvoked from the global binary still pins the project lock to theglobal's version. The fix is a per-target version, but an unreadable version falls back to an
absolute
SKILLS_DIRpath, which would write a machine-specific path into a committed lock — theportability problem The
argent initskills installation breaks skills.lock files checked into repos #208 fixed. Needs its own decision.uninstall.tscomputes orphans from the running package the same way, so it can strand newer skills.Same root cause, different command.
🤖 Generated with Claude Code