Skip to content

feat(index): doc_candidates switch for the doc-code candidate scoring - #2610

Merged
DeusData merged 1 commit into
mainfrom
feat/doc-candidates-switch
Oct 12, 2026
Merged

DeusData merged 1 commit into
mainfrom
feat/doc-candidates-switch

Conversation

@DeusData

Copy link
Copy Markdown
Owner

The doc↔code candidate scoring added in #2574 (doc_section_worker, inside semantic_edges) is the largest indexing cost on big repositories since v0.11.0. This adds a switch for it, on by default, so very large repositories can skip it.

Why

For each documentation section, the pass gathers every function that shares a key term with it (key terms: held by at most funcs/20 functions), computes a full dot product for each, and keeps the top ten. Its cost grows with sections × functions. Measured on 11 corpora, semantic_edges on the 10-09 main vs 4d9592b5, macOS arm64:

corpus functions doc sections extra time
perl, php, rust, c, kotlin ≤ 12.5k ≤ 562 ~0
typescript, django, go 31k–91k 4.0k–6.6k +0.1 to +1.0 s
java 325k 8.9k +12.6 s
csharp 451k 8.2k +14.6 s
Linux kernel 784k 28.1k +174 s

During that phase the kernel's samples are all in doc_section_worker. The candidates feed a real MCP feature: sections show the functions and files they may describe, and functions and files show the sections that may describe them, each with a judged probability. So the default stays on.

What

  • Setting doc_candidates (boolean, default true): codebase-memory-mcp config set doc_candidates false.
  • index_repository argument doc_candidates (optional boolean): overrides the setting for one index. The handler resolves the value once and passes it to a daemon job or supervised worker as an argument, like the resource policy. The session auto-index reads the setting.
  • Off: the scoring is skipped (pass.semantic.doc_candidates skipped=doc_candidates_off), and no doc_link_candidates rows are stored. Nodes and edges are unchanged. A delta-route reindex, which otherwise keeps the clone's candidate rows, replaces them with none, so turning it off leaves no stale candidates.
  • Docs: the README config list and docs/CONFIGURATION.md.

Checks

  • Default unchanged: django indexed by 4d9592b5 and by this branch. Nodes (63,441), edges (307,584) and candidate rows (11,304) are hash-identical.
  • Off: django nodes and edges are hash-identical to the default, with 0 candidate rows. On the Linux kernel, semantic_edges drops from 191.9 s to 17.9 s and the whole index takes 210.9 s (v0.11.0: ~234 s), with 0 candidate rows.
  • New tests. Each fails on its assertion when the switch has no effect:
    • pipeline_doc_candidates_off_stores_none_and_keeps_graph;
    • pipeline_doc_candidates_off_clears_rows_on_delta;
    • tool_index_repository_doc_candidates_switch: argument false → none; setting default → rows; setting false → none; argument true over setting false → rows.
  • pipeline, mcp and runtime_settings suites: 725 passed, including the tool-metadata byte budget.
  • cppcheck 2.20 and clang-format are clean on the changed sources.
  • The full local 3-OS ladder (macOS, Linux arm64, Windows arm64) is running alongside this PR's CI.

The doc<->code candidate scoring (#2574, doc_section_worker in
semantic_edges) scores every documentation section against the functions
that share a key term with it, key terms being those held by at most
funcs/20 functions. Its cost therefore grows with sections x functions.
Measured on 11 corpora (10-09 main vs 4d9592b, semantic_edges): under a
second up to ~100k functions, +12.6 s on java (325k), +14.6 s on csharp
(451k), +174 s on the Linux kernel (784k functions, 28k sections), almost
all of the kernel's regression against v0.11.0.

A new boolean setting, doc_candidates (default true), and an optional
index_repository argument of the same name switch the scoring off: no
candidate rows are stored and nodes and edges are unchanged. The argument
wins over the setting. The index handler resolves the value once and
passes it to a daemon job or supervised worker as an argument, the same way
as the resource policy, so every route sees one decision; the session
auto-index reads the setting. With the switch off, a delta-route reindex
replaces the candidate rows it would otherwise keep, so turning it off
leaves no stale candidates. The default output is unchanged.

Tests (each fails on its assertion when the switch has no effect):
- pipeline_doc_candidates_off_stores_none_and_keeps_graph: no rows, same
  node and edge counts as the default run, which has rows;
- pipeline_doc_candidates_off_clears_rows_on_delta: a closure-repair
  reindex with the switch off removes the earlier rows;
- tool_index_repository_doc_candidates_switch: argument false -> none;
  setting default -> rows; setting false -> none; argument true over
  setting false -> rows.

Proof, release-built binaries on macOS arm64 (M5 Pro 18c/48 GB):
- django, main 4d9592b vs this change with defaults: nodes (63,441),
  edges (307,584) and doc_link_candidates rows (11,304) hash-identical;
- django with doc_candidates false: nodes and edges hash-identical, no
  candidate rows;
- Linux kernel with doc_candidates false: semantic_edges 17.9 s (191.9 s on
  main, 17.6 s before #2574), whole index 210.9 s (v0.11.0: ~234 s), no
  candidate rows.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData
DeusData merged commit 08be028 into main Oct 12, 2026
13 of 17 checks passed
yermakoffivan pushed a commit to yermakoffivan/codebase-memory-mcp that referenced this pull request Oct 13, 2026
Doc-section -> code candidates (doc_link_candidates, DeusData#2574/DeusData#2610) are the
documentation-in-the-graph feature of this release. They now run by
default for full indexes and not for moderate or fast ones; an explicit
`doc_candidates` argument, or a `doc_candidates` setting the user set,
overrides the default either way in every mode.

- pipeline: the switch is unset/on/off; unset means on only when the
  effective mode is full.
- MCP: forwards only an explicit choice (argument, then a user-set config
  value) to daemon jobs and workers; daemon auto-index and watcher/UI jobs
  now carry a user-set value too (their workers have no config, so
  `doc_candidates false` was ignored there).
- fast + doc_candidates on runs the semantic pass's corpus phases and the
  doc phase only: candidates, and still no SIMILAR_TO or
  SEMANTICALLY_RELATED edges.
- config: `doc_candidates` joins the config key table (default `auto`);
  `config set doc_candidates ...` was refused as an unknown key before.
  README, docs/CONFIGURATION.md and the MCP schema describe the default.

Proof (macOS arm64, django fb11376):
  full 11,304 rows · moderate 0 · fast 0 · full+off 0 · moderate+on 1 ·
  fast+on 1 (moderate/fast discovery skips docs/) -- nodes and edges are
  identical between on and off in every mode; fast+on adds 0 semantic
  edges.
- New tests (RED on the old code): pipeline
  pipeline_doc_candidates_default_follows_mode,
  pipeline_doc_candidates_explicit_wins_over_mode,
  pipeline_doc_candidates_fast_on_legacy_incremental; mcp
  tool_index_repository_doc_candidates_follow_mode,
  tool_index_repository_doc_candidates_forwarded_only_when_explicit;
  daemon_application
  daemon_application_programmatic_index_forwards_set_doc_candidates.
- mcp 374 passed (4 Windows-only skips), daemon_application 73 passed,
  pipeline/store_arch/cli/runtime_settings/index_policy/pipeline_frozen
  green on the release tree.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant