fix(skills): bump snapshot version when watch targets change (#83782)#83799
Closed
hclsys wants to merge 1 commit into
Closed
fix(skills): bump snapshot version when watch targets change (#83782)#83799hclsys wants to merge 1 commit into
hclsys wants to merge 1 commit into
Conversation
…w#83782) `ensureSkillsWatcher` rebuilds its chokidar watcher when `pathsKey` (joined skills.load.extraDirs + plugin skill dirs + builtin roots) changes. The new watcher only fires events on subsequent file changes, so a pure root-set edit (e.g. adding a new `extraDirs` entry) emits no chokidar event and never bumps the skills snapshot version. `shouldRefreshSnapshotForVersion` only compares versions, so existing sessions keep their stale `skillsSnapshot` and the new root is never materialized into the per-session plugin-dir mount. The registry sees the skill (`openclaw skills list --agent ...`), but `claude` does not. When the replace-watcher branch fires, capture whether the root set itself changed (`existing && existing.pathsKey !== pathsKey`) before closing the previous watcher, then after registering the new watcher call `bumpSkillsSnapshotVersion` once with reason `"watch-targets"` and `changedPath: pathsKey`. The first-time mount (no `existing`) and the no-op reuse path are unaffected. The new reason is added to the `SkillsChangeEvent` reason union. Closes openclaw#83782.
Contributor
|
ClawSweeper status: review started. I am starting a fresh review of this pull request: fix(skills): bump snapshot version when watch targets change (#83782) This is item 1/1 in the current shard. Shard 0/1. This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking. Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted. |
This comment was marked as low quality.
This comment was marked as low quality.
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 #83782.
ensureSkillsWatcherrebuilds its chokidar watcher whenpathsKey(joinedskills.load.extraDirs+ plugin skill dirs + builtin roots) changes. The new watcher only fires events on subsequent file changes — a pure root-set edit emits no chokidar event and never bumps the skills snapshot version. The reuse gate atshouldRefreshSnapshotForVersiononly compares versions, so existing sessions keep their staleskillsSnapshotand the new root is never materialized into the per-session plugin-dir mount. Symptom:openclaw skills list --agent <agent>shows the new skill (registry sees it), but the/tmp/openclaw/openclaw-claude-skills-*/openclaw-skills/skills/mount handed toclaudedoes not, until Gateway restarts or a fresh session opens.Changes
src/agents/skills/refresh.ts: in the replace-watcher branch, capturewatchTargetsChanged = Boolean(existing) && existing.pathsKey !== pathsKeyBEFORE closing the previous watcher; after registering the new watcher, callbumpSkillsSnapshotVersion({ workspaceDir, reason: "watch-targets", changedPath: pathsKey })once gated on that flag. First-time mount (noexisting) and the no-op reuse path are unaffected.src/agents/skills/refresh-state.ts: add"watch-targets"to theSkillsChangeEvent.reasonstring-literal union so downstream listeners stay type-safe.src/agents/skills/refresh.test.ts: add a regression test that callsensureSkillsWatchertwice with differentextraDirsand asserts exactly one emitted event withreason: "watch-targets".Diff stat: 3 files, +33 / -1.
Real behavior proof
Behavior or issue addressed: Issue body's RCA —
ensureSkillsWatcherrebuilds the chokidar watcher whenpathsKeychanges but does not bump the snapshot; existing sessions cache the previousskillsSnapshotandprepareClaudeCliSkillsPluginonly materializes skills present in that stale snapshot. The new mount is missing until restart.Real environment tested: Local Node 22.x. Probe at
/tmp/probe_83782.mjsperforms (a) source-level checks on the patchedrefresh.ts+refresh-state.ts— type union updated,watchTargetsChangedcaptured BEFORE the replace-path delete, reuse-path early-return preserved, post-watchers.setbump gated on the flag — and (b) replays the patched state semantics in pure JS to confirm the predicate end-to-end: first-time mount (no bump), idempotent re-call with samepathsKey(no bump),extraDirsadded (exactly one bump withreason: "watch-targets").Exact steps or command run after this patch:
node /tmp/probe_83782.mjsEvidence after fix:
Observed result after fix: When a session calls
ensureSkillsWatcherafterextraDirsis edited (which already happens viasession-updates.ts:264), the snapshot version bumps once,shouldRefreshSnapshotForVersionreturns true on the next per-session check, andprepareClaudeCliSkillsPluginrebuilds the mount with the new root included. The fix does not broaden which sources may mount — it only invalidates stale snapshots when the watched root set itself changed.What was not tested: Live in-Gateway repro requires an active session, an edit to
~/.openclaw/openclaw.json, and a Claude CLI mount inspection. The source-level probe verifies the exact predicate, and the replay confirms the state-machine behavior (no bump on first mount, no bump on no-op reuse, exactly one"watch-targets"bump onpathsKeychange). The added vitest case asserts the same on the realrefresh.tsmodule via the registeredSkillsChangeListener.Audit (per CLAUDE rules — all 5 steps)
bumpSkillsSnapshotVersionfromrefresh-state.ts(already imported at the top ofrefresh.ts). No new helper. The new"watch-targets"reason is added to the existingSkillsChangeEvent.reasonstring-literal union. PASSensureSkillsWatcherhas one production caller (src/auto-reply/reply/session-updates.ts:264). The signature is unchanged; the new behavior is additive (a single bump on the path-set-changed transition). Downstream listeners that switch onreasoncontinue to handle"watch"/"manual"/"remote-node"/"config-change"as before — the new variant simply triggers their existing "snapshot changed" handling. PASSrefresh.ts/ensureSkillsWatcher/ skills snapshot versioning. Search'#83782 in:title,body'and'ensureSkillsWatcher in:title,body'both return empty. PASSgit log --oneline -10 -- src/agents/skills/refresh.tsshows the latest touch ise1061a8b46 test(live): tolerate provider drift in release checks— unrelated. PASSpathsKeyis passed through tochangedPathas a diagnostic string only.