Found during dogfooding v3.16.0
Severity: High
Command: codegraph embed <dir> (discovered here), but the root cause is shared by every command
Reproduction
# Run from a directory OTHER than the target project (e.g. a separate npm install dir,
# a CI runner's default cwd, or any MCP/multi-repo-style invocation):
cd /some/other/directory
# Target project has a .codegraphrc.json configuring the new v3.16.0 remote
# embedding provider feature:
cat /path/to/project/.codegraphrc.json
# {
# "embeddings": { "provider": "openai" },
# "llm": { "baseUrl": "http://my-embedding-server/v1", "apiKey": "..." }
# }
codegraph build /path/to/project
codegraph -v embed /path/to/project --db /path/to/project/.codegraph/graph.db -m minilm
Expected: routes through the remote OpenAI-compatible endpoint (embeddings.provider: "openai" in the target project's config).
Actual:
[codegraph] Building embeddings for 5086 symbols (strategy: structured)...
[codegraph] Embedding 5086 symbols...
[codegraph] Loading embedding model: Xenova/all-MiniLM-L6-v2 (384d)...
No "via remote provider" — it silently falls back to loading the local HuggingFace model, completely ignoring embeddings.provider. Additionally, embed's own validate() (src/cli/commands/embed.ts:56-68) is supposed to reject with "embeddings.provider is set to ... but no model is configured" when provider is set without embeddings.model and no --model flag — that check also never fires, because it reads the same stale/empty config.
Root cause
src/cli/index.ts:53-54 builds one shared ctx object passed to every command's validate()/execute(), using the config export from src/cli/shared/options.ts:
let _config: CodegraphConfig | undefined;
const config: CodegraphConfig = new Proxy({} as CodegraphConfig, {
get(_t, prop: string) {
if (_config === undefined) _config = loadConfig(process.cwd());
return _config[prop as keyof CodegraphConfig];
},
}) as CodegraphConfig;
This is a process-wide singleton, lazily resolved exactly once from process.cwd(), regardless of any positional dir argument or --db <path> flag a command receives. So codegraph embed /path/to/project invoked from a different cwd reads (or fails to find) .codegraphrc.json at the invoking directory, not /path/to/project.
This is the same architectural issue already partially fixed at the data-query layer in #1605 / #1881 / #2017 (via resolveConfigForDbPath() in src/db/index.ts), but those fixes only cover functions that call loadConfig() directly with a customDbPath — they don't touch the CLI dispatcher's ctx.config, which many commands (including embed's validate()/execute()) read directly instead of going through resolveConfigForDbPath().
Because ctx.config is shared process-wide, this affects every command that reads it directly rather than re-deriving config from its own customDbPath — at minimum embed (embeddings.provider/embeddings.model), and via resolveNoTests()/resolveQueryOpts() in the same file, the default -T/--no-tests behavior (config.query.excludeTests) for any command invoked with a dir/--db argument pointing elsewhere.
Suggested fix
Apply the same resolveConfigForDbPath(customDbPath) pattern used for #1881/#2017 to embed.ts's validate()/execute() (and any other command reading ctx.config directly for a value that should be project-scoped), deriving config from the command's own --db/dir argument rather than the shared cwd-pinned singleton. resolveNoTests()/resolveQueryOpts() in src/cli/shared/options.ts likely need the same treatment, since they're used broadly across commands.
Impact
This means the entire v3.16.0 headline feature (configurable remote embedding provider) silently does nothing whenever codegraph embed is invoked from outside the target project's own directory — a very common pattern (CI, multi-repo tooling, MCP-adjacent workflows, or simply passing a directory argument instead of cd-ing first).
Found during dogfooding v3.16.0
Severity: High
Command:
codegraph embed <dir>(discovered here), but the root cause is shared by every commandReproduction
Expected: routes through the remote OpenAI-compatible endpoint (
embeddings.provider: "openai"in the target project's config).Actual:
No "via remote provider" — it silently falls back to loading the local HuggingFace model, completely ignoring
embeddings.provider. Additionally,embed's ownvalidate()(src/cli/commands/embed.ts:56-68) is supposed to reject with"embeddings.provider is set to ... but no model is configured"whenprovideris set withoutembeddings.modeland no--modelflag — that check also never fires, because it reads the same stale/empty config.Root cause
src/cli/index.ts:53-54builds one sharedctxobject passed to every command'svalidate()/execute(), using theconfigexport fromsrc/cli/shared/options.ts:This is a process-wide singleton, lazily resolved exactly once from
process.cwd(), regardless of any positionaldirargument or--db <path>flag a command receives. Socodegraph embed /path/to/projectinvoked from a different cwd reads (or fails to find).codegraphrc.jsonat the invoking directory, not/path/to/project.This is the same architectural issue already partially fixed at the data-query layer in #1605 / #1881 / #2017 (via
resolveConfigForDbPath()insrc/db/index.ts), but those fixes only cover functions that callloadConfig()directly with acustomDbPath— they don't touch the CLI dispatcher'sctx.config, which many commands (includingembed'svalidate()/execute()) read directly instead of going throughresolveConfigForDbPath().Because
ctx.configis shared process-wide, this affects every command that reads it directly rather than re-deriving config from its owncustomDbPath— at minimumembed(embeddings.provider/embeddings.model), and viaresolveNoTests()/resolveQueryOpts()in the same file, the default-T/--no-testsbehavior (config.query.excludeTests) for any command invoked with adir/--dbargument pointing elsewhere.Suggested fix
Apply the same
resolveConfigForDbPath(customDbPath)pattern used for #1881/#2017 toembed.ts'svalidate()/execute()(and any other command readingctx.configdirectly for a value that should be project-scoped), deriving config from the command's own--db/dirargument rather than the shared cwd-pinned singleton.resolveNoTests()/resolveQueryOpts()insrc/cli/shared/options.tslikely need the same treatment, since they're used broadly across commands.Impact
This means the entire v3.16.0 headline feature (configurable remote embedding provider) silently does nothing whenever
codegraph embedis invoked from outside the target project's own directory — a very common pattern (CI, multi-repo tooling, MCP-adjacent workflows, or simply passing a directory argument instead ofcd-ing first).