feat(memory-health): fix Onboarding Console scoring the wrong project - #1
Open
Tigon32 wants to merge 1 commit into
Open
feat(memory-health): fix Onboarding Console scoring the wrong project#1Tigon32 wants to merge 1 commit into
Tigon32 wants to merge 1 commit into
Conversation
ruvnet-brain's Onboarding Console ("Your memory, proven" card) is correct on
the very first request, but every subsequent read is served from an on-disk
cache refreshed by a detached child process. kickRefresh() spawns that child
with `cwd: REPO` -- the plugin's own install directory -- instead of the
server's actual launch directory, so the refresh always scores the PLUGIN's
own (memory-store-less) directory and writes that wrong result into the
cache served to every subsequent request.
Measured live (2026-07-24): a project with a genuine, 122MB, actively-written
.swarm/memory.db scored 33/100, "Liveness: fail -- this project has no
memory store yet", because the number shown was ruvnet-brain's own
directory, never the project's.
The console's server process never calls process.chdir() anywhere in the
file (grepped, zero hits), so process.cwd() read at the moment
kickRefresh() fires is always identical to the cwd the server was launched
with -- the same value startServer()'s own default parameter and the
--serve/--print-state CLI branches already use. No argument threading
needed through kickRefresh()'s signature or either of its two call sites.
Adds the `memory-health` patch target (patcher, commands, README, ADR-025),
wires it into plugin-registry/plugin-compose/CLI/docs the same way
verify-interface and design-wall are wired, and adds a behavioural test
(test/memory-health.mjs) that proves: the buggy anchor is unique in the real
vendor file, the fix touches ONLY the intended spawn call (the file's other
8 REPO usages are verified byte-identical before and after), apply is
idempotent, and uninstall restores byte-identical vendor.
Verified end-to-end on a live install: patched the real
~/.claude/plugins/marketplaces/ruvnet-brain/scripts/onboarding-console.mjs,
restarted the console server from a real project directory, and confirmed
the on-disk cache written by the background refresh child now reports that
project (score 67, liveness: ok) instead of the plugin's own directory
(score 33, liveness: fail).
Full test suite: 0 new failures. Two suites (reporting.mjs R4, an
unrelated missing `memory` patch-target precondition on this machine; and
design-wall.mjs DW1/DW4) fail identically on main before this change --
confirmed via a clean worktree at the same commit, both with and without
this patch applied.
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.
The bug
ruvnet-brain's Onboarding Console (the "Your memory, proven" card, served via/brain-console)is correct on the very first request —
startServer({ cwd = process.cwd() })captures the reallaunch directory, and
gatherMemory(cwd)checks that project's.swarm/memory.db. But every readafter that is served from an on-disk cache, refreshed by a detached child process that
kickRefresh()spawns at most once per 15 seconds:REPO(path.dirname(__dirname), the plugin's own install directory) is hardcoded as the child'scwd. The--refresh-cachebranch callsgatherState(process.cwd()), which resolves toREPObecause that's the cwd the child was spawned with — never the project actually being served.
REPOhas no project.swarm/memory.dbof its own, so the health check genuinely fails, for thewrong project, and that wrong result is written straight into the on-disk cache and served to
every subsequent request, including the very next page load, re-derived every 15s by the next kick.
Measured live (2026-07-24): a project with a genuine, 122MB, actively-written
.swarm/memory.db(separately confirmed healthy via the AgentDB/ruflo memory bridge: 10,875 entries, 16,390 patterns
learned) scored 33/100, with
"Liveness: fail — this project has no memory store (.swarm/memory.db) yet"— because the number being shown was ruvnet-brain's own plugin directory,never the project's.
The fix
Read the live
process.cwd()at spawn time instead of the hardcodedREPOconstant:This is sound because the console's server process never calls
process.chdir()anywhere in thefile (grepped, zero hits) — so
process.cwd()read at the exact momentkickRefresh()fires isalways identical to the cwd the server was launched with, the same value
startServer()'s owndefault parameter and the
--serve/--print-stateCLI branches already use. No argument threadingneeded through
kickRefresh()'s signature or either of its two call sites.Single literal edit — the file's other 8 uses of
REPO(CONSOLE_DIR, the SBOM path, thescript-runner's own legitimate
cwd: REPO,gatesSurvey, etc.) are untouched, verifiedbyte-for-byte in the test.
What's in this PR
lib/memory-health/patcher.mjs+commands.mjs+README.md— the new patch target, followingthe exact shape of the existing
verify-interface/design-walltargetsdocs/adr/ADR-025-memory-health-kickrefresh-inherits-server-cwd.md— full context/decision/consequenceslib/plugin-registry.mjs,lib/plugin-compose.mjs,bin/cli.mjs, rootREADME.md,AGENTS.mdtest/memory-health.mjs— behavioural test against the real vendor file (via its.rsp-backupifinstalled, else skips if
ruvnet-brainisn't present): proves the buggy anchor is unique, the fixtouches ONLY the intended spawn call (all 8 other
REPOusages verified unchanged), apply isidempotent, and uninstall restores byte-identical vendor
test/fixtures.mjs— added thememoryHealthrecognizer tolooksPatched(needed sopristineBytes()can tell a genuinely-patched file from a pristine one for this target)scripts/run-tests.sh— addedmemory-healthtoSUITESVerification
node test/memory-health.mjs— all 8 checks passscripts/run-tests.sh) — no new failures introduced. Two pre-existing failures(
reporting.mjsR4 — amemorypatch-target precondition not installed on my machine;design-wall.mjsDW1/DW4) reproduce identically onmainat the same commit, with and withoutthis patch — confirmed via a clean worktree
node scripts/md-lint.mjs— 0 problems across 44 files~/.claude/plugins/marketplaces/ruvnet-brain/scripts/onboarding-console.mjs, restarted theconsole server from a real project directory, and confirmed the on-disk cache written by the
background refresh child now reports that project (score 67, liveness: ok) instead of the
plugin's own directory (score 33, liveness: fail)
Upstream
Filed as
ruvnet-brain (memory-health card)alongside this patch, same convention asverify-interface/design-wall's existing upstream issue links.