Semantic search over a Markdown knowledge base, served over MCP.
quality filter は各チャンクを 3 つのシグナルから採点し、既定では 0.3 未満を隠す:
長さ (30 文字未満で -0.6)、定型語のみ (-0.5)、弱い構造 (80 文字未満の 1 行で
-0.3)。
このうち 2 つは短さを「中身が薄いこと」の代理として読む。バイナリ形式由来の
チャンクは既に両方を免除されていた — ページ / シート / スライドが短いのは形式の構造で
あって薄い節ではないから。ソースコードの定義チャンクは免除されていなかった。免除が
Parser::is_binary() に載っていて、code parser はバイナリ parser ではないからである。
ADR-0012 は その結果を「結果と代償」に記録した: 1 行の宣言は閾値を割って filter される、 「名前以外の情報を持たず、その名前が指すものは別に index される」から。公開している behavior doc も同じことを書き、spec は再検討の条件を「実 KB で意味のある短い定義が 落ちる報告」と定めた。
この主張には根拠があったし、正しかった — 本リポジトリの Rust ソースに対しては。
閾値を割ったのは pub mod x; と unit struct だけだった (母集団と件数は ADR-0012 の
同じ節、出典は feature-56-spike-2026-08-27.md §5)。
見たのは Rust だけである。 v1.3.0 は 2 番目の言語の grammar を初めて配った版であり、
しかも再検討の条件を満たす報告は原理的に届かない — 作者が常用している production KB は
コード解析を無効にしているからである。
母集団を 1 コーパス 435 定義から 3 コーパス 11,002 定義へ広げて測り直した
(via: python census.py <db> <rows-out>、記録は
av-07-short-definition-census-2026-09-04.md §5):
| corpus | 定義数 | 閾値割れ | 名前だけ | 値を持つ |
|---|---|---|---|---|
本リポジトリ src (Rust) |
3362 | 70 | 70 | 0 |
本リポジトリ tests (Rust) |
814 | 19 | 19 | 0 |
CPython Lib/*.py (Python) |
6826 | 723 | 2 | 721 |
Rust は 8 倍以上の母集団で元の結論をそのまま再現した。Python は再現しなかった。
Python の index から落ちているのは MAXYEAR = 9999 / HAVE_ARGUMENT = 44 /
SF_NOUNLINK = 0x00100000 — pickle の opcode、token の ID、stat のフラグ、バージョン
文字列である。値こそが情報で、その値はどこにも別途 index されていない。 docs が
書いた理由はこれらに対して不十分なのではなく、端的に偽である。
つまりこの制限は 1 言語で観測したことを全言語へ一般化したもので、それが到達可能に なった版を出荷した直後だった。
Parser::is_binary() は既に 2 つ目の問い (get_document がどの size
cap を当てるか) に答えており、その答えはここから遠くまで流れていく。type ShardId = u64; は返り、pub mod x; は
隠れたままにする。選択肢 4 を採る。
選択肢 2 を採らない理由 — そもそも成立しない。 既定閾値で計算すると、減点 1 個では
決して隠れない (長さのみ 0.4 / 構造のみ 0.7 / 定型語のみ 0.5)。さらに
「30 文字未満」は必ず「80 文字未満」なので、長さ由来の 2 減点は必ず同時に立つか
同時に立たないかである。したがってチャンクが落ちるのは 2 つとも立った時だけで、
どちらか一方を免除すれば影響を受けるチャンクは全部戻る。pub mod x; は 10 文字
(via: echo -n 'pub mod x;' | wc -m)、type ShardId = u64; は 19 文字で、まったく
同じ 2 減点を取る。減点の粒度では区別できない。選択肢 2 は「docs が嘘のままの選択肢
4」であり、この項目を再提起する人が最初に手を伸ばす形でもある。
選択肢 3 を採らない理由。 2 つを分けるには定義の中身を読むか、symbol_kind の
値ごとの言語別テーブルを持つかになる。前者は一度落とした第 3 の軸そのもので、後者は
「grammar をデータのままにする」方針が避けているものである。得られるものは上限が
分かっている: Rust 2 コーパスの名前だけの宣言 89 件 (上表の 70 + 19) を再び隠す代わりに、
この変更が取り戻す 721 件 (同表) の値を捨てることになる。
選択肢 1 を採らない理由。 全言語が Rust と同じように振る舞う限りは妥当な答えだが、 それは 1 リリース前に成り立たなくなった。
免除は既存の bool を広げるのではなく新しい QualityProfile (Text / Binary /
Definition) に載せる。「このチャンクにどの減点が当たるか」という問いの答えを 1 か所に
保ち、size cap も決めているフラグに相乗りさせないため。
1.0、min_quality は
1.0 で clamp され、チャンクが落ちるのは閾値を下回った時だけ。したがって
pub mod x; だけを外して他を残せる値は存在しない。残る手はパスでの除外
(先頭 ! の path_globs)、tags_any: ["code"] で反対側を取ること、
そしてその木でその言語を有効にしないこと。const は gap の行埋めへ回り、この決定は届かない
(via: grep -n 'definition\.' <registry>/tree-sitter-rust-0.24.2/queries/tags.scm)。TODO のチャンクは、何が
作ったものであれ薄い。免除するのは長さ由来の 2 つだけ。} だけで見出しが関数名
(bm25 は見出しを重く見る) のチャンクができる。従来は quality filter が隠していたが、
免除するとそれが返り始める。末尾の片が短さ閾値未満なら 1 つ前の片に畳み込むように
し、本決定が前提にしている「symbol_kind を持ち、かつその閾値未満のチャンクは、
定義まるごとである」を真にした。gap / fallback の片は変えていない — symbol_kind を
持たないので従来どおり減点され、ADR-0012 はその薄い末尾を残すことを求めている。
本リリース以前に作った index は当時のチャンクを保つ: backfill が採点し直すのは
保存済みのチャンクで、内容の変わっていないファイルは切り直されない。したがって旧
splitter が切った末尾片はそのまま残り、symbol_kind を持つ以上ほかと同じく免除される。
backfill は短い定義チャンクを昇格させたときにそう警告し、groove index --force を
案内する。免除を「切り直し済みであること」で gate すると、その形を持たない大多数の
既存 index にまで再 embedding を強いるので採らない。groove index で追随する。 これまで列の
既定値のままのチャンクしか見ていなかった backfill が、symbol_kind を持つチャンクも
見るようになる。書き換わるのは 1 列だけなので再 embedding は起きず、--force も
要らない。採点は grooveseek/src/quality.rs、既存 index を移行させる backfill は
grooveseek/src/db/meta.rs の backfill_quality。この決定を決めた census — 母集団と
判定基準はどちらも数える前に固定した — は av-07-short-definition-census-2026-09-04.md
で、本リポジトリではなく開発用ノート側にある。