feat(memory-plugin): add ov-memory-doctor skill and diagnostics script for Claude Code and Codex - #4389
Merged
ZaynJarvis merged 4 commits intoAug 27, 2026
Conversation
…t for Claude Code and Codex
… in the doctor skill
…cal deployments When the resolved url is loopback the doctor now inspects the server side: ov.conf startup blockers (plugin-only keys the server rejects, dev mode on a non-loopback bind, empty root_api_key, port mismatch, relative workspace, unexpanded $VAR secrets, provider credential rules), the server process and port owner (pid file, lsof/ss, docker container and its /app/.openviking mount), the vector index's recorded embedding vs the configured one, the server log when log.output is a file, and GET /ready. Remote servers get the /ready probe only. The docker pending_initialization stub is recognised in the Connection section. Skills, references and READMEs describe the new section; provider-level validation stays with openviking-server doctor.
…he port, plugin-only ov.conf keys and /ready The section replicated the server's own config validation (top-level and server.* key allowlists, provider credential rules, vlm, workers) and inspected the pid file, docker mounts, systemd, the vector collection metadata and the server log. All of that is what openviking-server reports itself at startup or what `openviking-server doctor` covers, and the allowlists would drift with every new config field. Keep what the server cannot tell the client: whether anything listens on the port, the plugin-only ov.conf keys the server refuses to start on, and GET /ready. doctor-core.mjs is now synced only to the plugins that ship a doctor script; the opencode and zcode copies were never imported.
ZaynJarvis
approved these changes
Aug 27, 2026
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.
Description
Adds a client-side troubleshooting skill,
ov-memory-doctor, to the Claude Code and Codex memory plugins, backed by ascripts/ov-memory-doctor.mjsreport script and a shareddoctor-core.mjsmodule.The plugins fail silently in three places that users cannot see from inside the harness: the install (marketplace registration, enablement, hooks, MCP wiring — when wrong, no hook ever runs and nothing is logged), the client config (a malformed
ovcli.confis indistinguishable from a missing one and silently disables the Claude Code plugin / falls back tohttp://127.0.0.1:1933on Codex; a strayOPENVIKING_*env var silently overrides the file), and the connection (GET /healthreturns 200 even with an invalid key, so the statusline shows green while every real request 401s). The installer performs no URL/API-key validation at all, so a wrong URL or dead key installs "successfully" and only surfaces later as empty recall. The existing/ovcommand probes/healthonly and never/mcp.The doctor script runs the install, configuration, connection, server-health and recent-activity checks in one pass and prints a fix for every finding; the skill tells the agent when to run it, how to map findings to causes, which targeted checks to run when the report is not conclusive, and a set of rules for things that go wrong during troubleshooting itself (never print the key,
/health200 is not auth,ov doctoris server-side,setup.mjscannot bootstrap on older versions,debug-capture.mjscorrupts the live capture cursor, etc.).API keys are never printed in full: three-segment keys are shown as
account=<decoded> user=<decoded> secret=abcd…wxyz(the first two segments are base64url identity, decoded so the operator can see which account/user the key claims), legacy keys asabcd…wxyz (64 chars).Human Involvement
Related Issue
N/A
Type of Change
Changes Made
examples/memory-plugin-shared/lib/doctor-core.mjs(new, synced to Claude Code / Codex viasync.mjs): API key display and shape checks, base URL lint, JSON config inspection that reports parse errors,OPENVIKING_*/ proxy / TLS env sweep, the server probe ladder (GET /healthunauthenticated →GET /healthwith credentials for the identity echo →GET /api/v1/system/statusfor a real 401/403 →GET /api/v1/fs/ls?uri=viking://~/memoriesfor tenant-data authorization and the resolved user space →POST /mcp tools/list) with interpretation, state-file and JSONL hook-log scanning, a Server health section (GET /readyinterpreted per subsystem; when the url is loopback also whether anything listens on the port and the plugin-only ov.conf keys —claude_code,codex,server.url— that makeopenviking-serverrefuse to start; the docker image'spending_initializationstub is recognised on/health), and report rendering with--jsonoutput and exit codes. Everything else server-side (config validation, live embedding probe, native engine, disk) is left toopenviking-server doctor.examples/claude-code-memory-plugin/scripts/ov-memory-doctor.mjs(new): install checks againstinstalled_plugins.json,known_marketplaces.json,~/.claude/settings.json(enablement, legacy merged hooks, statusline path, file-type marketplace, duplicate ids, missingskills/in a version-keyed cache), rc-file residue,claude plugin list --json; config resolution with per-field sources (mirrorsov-status.mjsrather than trustingloadConfig().configPath),isPluginEnabledverdict with the reason,root_api_keyfallback warning, bypass patterns, hook-budget checks; connection probes;~/.openviking/state, pending queue andcc-hooks.logevidence including MCP proxy url drift.examples/claude-code-memory-plugin/skills/ov-memory-doctor/{SKILL.md,reference.md}(new): trigger description, workflow, finding → cause → action table, targeted curl checks, fixing guidance and rules; reference with paths, resolution chains, auth modes, exact server / proxy error strings, state-file fields and a symptom catalogue.allowed-toolspre-approves the doctor script.examples/codex-memory-plugin/scripts/ov-memory-doctor.mjs(new): same shape for Codex —~/.codex/config.toml([features] plugin_hooks,[plugins."…"] enabled,[hooks.state]trust records for the four hooks, legacy sections),codex plugin list/marketplace list --json, cache version vs running copy, credential source mode (OPENVIKING_CREDENTIAL_SOURCE), auth mode vs the server'sauth_mode, recall/capture timeouts vs hook budgets,codex-plugin-stateorphan sessions andcodex-hooks.log.examples/codex-memory-plugin/skills/ov-memory-doctor/{SKILL.md,reference.md}(new): Codex variant (no${PLUGIN_ROOT}substitution in skill bodies, so the skill locates the script via the plugin cache /codex plugin list --json).examples/codex-memory-plugin/.codex-plugin/plugin.json: declares"skills": "./skills/". Every bundled Codex plugin declares this key and the OpenViking manifest did not, so the existingopenviking-memoryandov-experience-memoryskills were shipped but most likely never loaded. Version 0.7.6 → 0.7.7.examples/claude-code-memory-plugin/.claude-plugin/plugin.json,package.json: version 0.4.4 → 0.4.5. Both harnesses cache plugins by manifest version, so a new skill cannot reach users without a bump (this machine's cache still lacks theskills/directory added on 2026-08-17 under an unchanged 0.4.4).examples/memory-plugin-shared/{sync.mjs,sync.test.mjs}:doctor-core.mjssynced only to the two plugins that ship a doctor script (DOCTOR_SHARED_FILES); zcode gets an explicit file list instead of the whole directory.examples/memory-plugin-shared/doctor-core.test.mjs(key display, URL lint, error classification, JSON inspection, probe assessment, log scanning, report rendering), additions to bothmarketplace.test.mjs(skill files present,node --checkon the scripts, Codexskillskey), wired into.github/workflows/pr.yml;.github/scripts/stage-memory-plugin-marketplace.shrequires the new skill and script files.Testing
The full
node --testlist from.github/workflows/pr.ymlpasses (351 pass, 1 pre-existing skip).claude plugin validate --strictpasses for both plugins.Both doctor scripts were run live against a real server (
ov-dev, auth_mode=api_key) and with injected faults: invalid key (/health200 without identity +system/status401 +/mcp401 all flagged), unreachable port (ECONNREFUSED classified), URL with/api/v1suffix (lint +/health404), malformedovcli.conf(parse error reported, "plugin disabled" verdict with reason), freshHOME(no config / not installed), three-segment key with a mismatching configured account,Bearer-prefixed key, mangled base64url segment,--json,--offline; server side: loopback server withlsofport owner and/readyok, dead port, remote server (/readyonly), ov.conf withclaude_code/codex/server.urlblocks, stub servers answering/readywith failing checks,503 initializing, 404 and the dockerpending_initializationstub.Checklist
Screenshots (if applicable)
N/A
Additional Notes
'ov serve'proxy hint,credentialPathmisreport). The two PRs touch the same generatedscripts/shared/*files; whichever lands second needs anode examples/memory-plugin-shared/sync.mjsre-run.skills/once the manifest declares it could not be verified non-interactively; the change follows the bundled Codex plugins' manifests.docs/en/agent-integrationsstill documentserver.url/claude_code/codexas ov.conf settings, whichopenviking-serverrejects at startup (extra: forbid); the doctor warns about them, the docs are left for a follow-up.