[Bug] attachSkillSubscriber wired with bgLlm, not bgReflectLlm — skillEvolver dedicated client never invoked
Pre-submission checklist
- I have searched existing issues and this hasn't been mentioned before
- I have read the project documentation and confirmed this issue doesn't already exist
- This issue is specific to MemOS and not a general software issue
Bug Description
Summary
In core/pipeline/deps.ts, attachSkillSubscriber is wired with llm: bgLlm (the main model), never bgReflectLlm (the dedicated client built from the skillEvolver config block). As a result the skill subscriber's LLM-backed work (crystallization evaluation, evolution) always runs on the main model regardless of skillEvolver.* config, and skillEvolver.lastOkAt never updates because the dedicated client is never invoked.
Root cause
// deps.ts, attachSkillSubscriber call
const skillHandle = attachSkillSubscriber({
repos: deps.repos,
embedder: bgEmbedder,
llm: bgLlm, // ← BUG: should be bgReflectLlm
bus: buses.skill,
l2Bus: buses.l2,
rewardBus: buses.reward,
log: log.child({ channel: "core.skill" }),
config: algorithm.skill,
...
});bgReflectLlm is constructed earlier in the same file specifically from the skillEvolver config block, but is only read for metadata (health/overview endpoint), never passed into the skill subscriber itself.
Verified via git log -L on this call site: it has never been wired to bgReflectLlm since the v2.0 Reflect2Evolve rewrite (60b97444) — this is not a regression, it's never worked.
Relation to #2148
This is a distinct instance of the same wiring-bug class fixed in #2148 (PR #2151) — that issue was about captureRunner's reflectLlm slot getting the wrong client (skill-evolver client where main llm was needed). This issue is the opposite direction: the skill subscriber never gets the dedicated skillEvolver client it's supposed to have. Different call site, not fixed by #2151.
Impact
- Operators configuring a distinct
skillEvolver.*model (e.g. to isolate skill-evolution cost/quality from the main model) get silently ignored — the mainllmhandles it instead. skillEvolver.lastOkAtin the overview/health endpoint readsnullindefinitely on any install that relies on it as a liveness signal for skill evolution, even when skill evolution is running fine (just on the wrong model).
Suggested Fix
const skillHandle = attachSkillSubscriber({
...
llm: bgReflectLlm ?? bgLlm,
...
});Source: MemTensor/MemOS