Skip to content

Console learner-scope fix (#136) hardcodes the opposite direction; and ruflo 3.38.9's real --train now makes the stale card silently self-clearing #139

Description

@ObiWanKenobi

Follow-up to #136 (fixed in 71c2107, v4.0.48-dev). Two things the original diagnosis — mine — got structurally wrong, and one new hazard created by an unrelated upstream fix landing in ruflo. Neither is the release-boundary gap already tracked on #136.

1. The fix hardcodes the opposite direction

#136 read the learner with cwd: SYSTEM_HOME; the fix changed it to cwd: process.cwd(). Both are hardcodes. Neither asks which scope is actually in effect.

scripts/learn-flush.mjs already resolves this properly:

// :27-31
const configuredScope = process.env.RUVNET_LEARNING_SCOPE
  || loadRuntimePreferences({ cwd: PROJECT }).values.learningScope;
const LEARNING_SCOPE = ['off', 'project', 'user'].includes(configuredScope)
  ? configuredScope : 'project';        // default: project

// :54
const QUEUE_ROOT = LEARNING_SCOPE === 'user'
  ? path.join(HOME, '.cache', 'ruvnet-brain', 'learn')
  : path.join(PROJECT, '.swarm', 'ruvnet-brain-learn');

// :137
cwd: LEARNING_SCOPE === 'user' ? HOME : PROJECT,

So the writer is scope-aware and the reader is not. process.cwd() happens to be correct only because project is the default. Set RUVNET_LEARNING_SCOPE=user and the bug inverts: the flush feeds ~/.claude-flow/neural while the console reads <project>/.claude-flow/neural. Same card, same false positive, opposite direction.

The durable fix is for the console to resolve scope the same way learn-flush.mjs does, so the two agree by construction rather than by coincidence.

2. Now that ruflo's --train is real, the stale card becomes self-clearing

This is the part I would flag first.

ruvnet/ruflo#2940 (--train was a no-op) is fixed — shipped in ruflo v3.38.9, which is current on npm. hooks_intelligence now genuinely calls distillLearning().

health-repair.mjs:198 still runs that command with cwd: HOME:

execFileSync(RUFLO, ['hooks','intelligence','--train'], { cwd: HOME, ... })

Previously this was harmless-but-useless: the remedy did nothing, so the card re-fired and stayed visible. Now the button works. On a default (project-scope) install it will really train the user store, move that store's lastAdaptation to 0s, and the card will disappear — while the learner the operator actually uses is untouched.

That converts a visible false positive into a silent one. A remedy that succeeds against the wrong target is materially harder to catch than one that visibly does nothing, and health-repair.mjs:20 states the discipline it breaks ("Every result is DERIVED from a re-measurement, never asserted from an exit code").

Reproduction — this machine, today

.console-runtime is still serving 4.0.36 (pre-fix), so onboarding-console.mjs:2219 is still cwd: SYSTEM_HOME. RUVNET_LEARNING_SCOPE is unset, so the flush is project-scope by default.

store trajectories stats.json mtime
~/.claude-flow/neuralwhat the card reads 1216 2026-08-03 17:48 (10.2 days)
<project>/.claude-flow/neural — where work lands 10309 seconds before the read

Card text: "last trained 10.1 days ago (1216 trajectories recorded)". .last-flush in the user queue is 1785767503 = 2026-08-03 17:48 — the same minute the user store froze, i.e. the moment scope stopped being user.

The flush itself is healthy — I assumed it had died and was wrong. Verified against a copied queue:

$ LEARN_QUEUE=/tmp/probe.jsonl node scripts/learn-flush.mjs --sync
learn-flush: fed 8/8 distinct actions to the project learner;
             36 distinct action(s) deferred to the next flush (queue kept, nothing discarded)

Worth noting for anyone else debugging this: running learn-flush.mjs by hand exits 0 silently, because SID falls back to default, the queue path doesn't exist, and :87 swallows it in a catch. That exit 0 proves nothing — the LEARN_QUEUE override is the only way to actually exercise it.

Also: the 7 session files accumulating in the project queue are not a failure. codex-hook-wrapper.mjs:23 allots learn-flush 2250ms, which fed 8 of 44 actions before deferring the rest. Capture outpaces the drain; nothing is lost.

Versions

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions