fix(doctor): stop asserting a version claim it cannot back up, and name the install it is not - #285
Merged
Merged
Conversation
1 task
kevintseng
force-pushed
the
fix/doctor-install-skew
branch
from
September 2, 2026 15:45
7e4af38 to
0059b11
Compare
18 tasks
…me the install it is not Dogfooding the published 4.8.3 found `memesh doctor` printing `[PASS] Update status — Version 4.8.2 is current.` on the npm-global install at the moment npm had already published 4.8.3. Nothing in that sentence was checkable: doctor never makes a live registry call (deliberately), it reads a cached update-check, and the cache was ~23h44m old — inside STALE_AFTER_MS, so still classified 'cached' rather than 'stale', so the claim printed unqualified. It was true when the cache was written and false when doctor read it, and a reader could not tell the difference. The row now says "As of the last check (23 hours ago), 4.8.2 was the latest version." — the same information, but falsifiable. The same machine had a second, larger problem doctor was structurally unable to see: the Claude Code plugin on 4.8.3 and the shell CLI on PATH on 4.8.2, so an agent and the human typing `memesh` in a terminal were running different code against one database. `inspectShellCli` already resolved the other install's path and said the two coexist; it never read that install's version. `readVersionFromInstalledBinary` now follows npm's bin symlink with `realpathSync` and walks up a bounded eight levels to the nearest package.json, trusting it only when its `name` matches — a longer walk risks reporting an unrelated package's version as memesh's own. When the shell CLI is behind, the row is a WARN naming both versions with channel-correct advice. Third: on a machine with both installs, the auto-updater can never fix the stale one. `spawnAutoUpdate` derives its channel from whichever copy's hook is executing, hooks are wired to the plugin copy, so the channel is always plugin-marketplace and the log is 45 consecutive SKIPPED lines — correct for the copy that logged it, silently incomplete for the user, whose npm-global copy is the one needing a manual `memesh update`. The npm-global plugin-cache row now says so, and mentions the cached-copy count once it exceeds two (size is deliberately not computed — `du` over a node_modules-sized tree on every doctor run is not worth the number). Refs #240-class (a check that states something it did not verify) [Verified-By: node scripts/run-tests-isolated.mjs tests/core/doctor.test.ts -> exit=0, "Tests 159 passed (159)"; mutation (shellIsBehind forced false, dist rebuilt) -> exit=1, "Tests 1 failed | 158 passed (159)"; restored -> exit=0, "Tests 159 passed (159)"; real machine, isolated MEMESH_DIR: doctor prints "[WARN] Shell CLI on PATH … it is running 4.8.2 — behind this install's 4.8.3"]
…ng it could not tell `detectPluginHost(packageRoot) ?? 'claude-code'` collapsed a legitimate `null` — "this path is not under any plugin cache" — into a confident answer. A Codex user whose cache location this process cannot resolve was told to run `memesh upgrade-plugin`, which does nothing for them, and nothing in the message marked it as a guess. When the host is detectable the advice still names only that host's command; when it is not, it names both. Same defect and same fix as the session-start banner's `pluginHostOf`, found the same way. The test that should have caught this was pinning the bug instead. Its fixture builds `packageRoot` under `os.tmpdir()`, which is not under any plugin cache, so it exercised precisely the undetectable-host branch — and asserted the Claude Code command was correct there. It now asserts both commands appear, and a second test builds a `packageRoot` under `.claude/plugins/cache/` and asserts the Codex command does NOT appear, so "always print both" cannot pass either. [Verified-By: node scripts/run-tests-isolated.mjs > /tmp/f285b.log 2>&1; echo exit=$? -> exit=0; "Test Files 229 passed | 1 skipped (230)", "Tests 3286 passed | 11 skipped (3297)", no Errors line] [Verified-By: npm run verify:release > /tmp/v285b.log 2>&1; echo exit=$? -> exit=0, ending "consumer install has no high-or-worse advisories"] [Verified-By: break-test, mutate -> run -> restore with byte-identical restore asserted by reading the file back: put `?? 'claude-code'` back -> exit=1 (only the named assertion failed, 159/160 still ran), restored -> exit=0] [Verified-By: MEMESH_DIR=<throwaway> eg check -> exit=0, "PASS ... rev=0059b110 ... doctor-install-skew: source_library required=CODE_COMPLETE observed=CODE_COMPLETE". changed=0 because the contract is frozen at this HEAD, so eg's uncovered-changed-paths check is vacuous here, as in every ledger in this batch]
kevintseng
force-pushed
the
fix/doctor-install-skew
branch
from
September 3, 2026 03:50
eb005c7 to
380b548
Compare
The `?? 'claude-code'` fallback it described only existed in this PR's own first commit and was replaced before the PR was opened; main never carried it. Left in, it would read as a real regression fixed by this release.
kevintseng
added a commit
that referenced
this pull request
Sep 3, 2026
The rebase onto origin/main (post-#285 merge)'s own conflict resolution took the wrong side for these two map files; a full rebuild produces the correct byte content.
kevintseng
added a commit
that referenced
this pull request
Sep 3, 2026
The rebase onto origin/main (post-#285 merge)'s own conflict resolution took the wrong side for these two map files; a full rebuild produces the correct byte content.
kevintseng
added a commit
that referenced
this pull request
Sep 4, 2026
The rebase onto origin/main (post-#285 merge)'s own conflict resolution took the wrong side for these two map files; a full rebuild produces the correct byte content.
kevintseng
added a commit
that referenced
this pull request
Sep 4, 2026
The rebase onto origin/main (post-#285 merge)'s own conflict resolution took the wrong side for these two map files; a full rebuild produces the correct byte content.
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.
Summary
Dogfooding the published 4.8.3 found
memesh doctortelling the user something false, and staying silent about something worse.It asserted a version claim it cannot back up. The npm-global install printed
[PASS] Update status — Version 4.8.2 is current.at a moment when npm had already published 4.8.3. Doctor never makes a live registry call (deliberately); it reads a cached update-check, and that cache was ~23h44m old — insideSTALE_AFTER_MS, so still classifiedcachedrather thanstale, so the claim printed unqualified. True when the cache was written, false when doctor read it, and nothing in the sentence let a reader tell the difference. The row now reads "As of the last check (23 hours ago), 4.8.2 was the latest version." — same information, falsifiable.It could not see that two installs were running different code. The Claude Code plugin was on 4.8.3 and the shell CLI on PATH on 4.8.2 — an agent and the human typing
memeshin a terminal running different code against one database.inspectShellClialready resolved the other install's path and reported that the two coexist; it never read that install's version. It now does, viareadVersionFromInstalledBinary: follow npm's bin symlink withrealpathSync, then walk up a bounded eight levels to the nearestpackage.jsonand trust it only when itsnamematches — a longer or unchecked walk would report an unrelated package's version as memesh's own.And the stale copy can never fix itself.
spawnAutoUpdatederives its channel from whichever copy's hook is executing; hooks are wired to the plugin copy, so the channel is alwaysplugin-marketplaceand~/.memesh/auto-update.logholds 45 consecutiveSKIPPEDlines — correct for the copy that logged it, silently incomplete for the user, whose npm-global copy is the one that needs a manualmemesh update. The npm-global plugin-cache row now says so, and mentions the cached-copy count once it exceeds two.Type of change
fix)Docs synced
CHANGELOG.md[Unreleased]→### Fixeddist/core/doctor.*regenerated (tracked build output)check-doc-claimsconfirms there is nothing to updateVerification
node scripts/run-tests-isolated.mjs tests/core/doctor.test.ts→ exit 0, 159 passed (149 pre-existing + 10 new)shellIsBehindnull,dist/rebuilt) → exit 1, 1 failed / 158 passed; restored → 159/159npm run lint0,npm run typecheck0,check-doc-claims0,npm run verify:release0eg checkPASS at base2d4c0f04—doctor-install-skewCODE_COMPLETE with a known_bad_mutation failure_pathMEMESH_DIR, the new row fires correctly:Test plan
CI on this head. After merge,
memesh doctoron any machine with both a plugin and a global install will name the skew instead of reporting PASS.