Semantic search over a Markdown knowledge base, served over MCP.
ADR-0014 は、chunker が定義単位の
chunk 化をやめるまでに定義が入れ子で居てよい深さの上限を置いた。定義のスコープを集める
walk が自分の歩数を数え、構文木の祖先を 64 個超えた最初の定義でそのファイルの定義単位
chunk 化は終わる。この上限は同時に、chunker が持つ唯一の再帰関数が暴走しない根拠でもあった。
emit_def は包含関係の木を降りるが、包含する定義は構文木の祖先でもあるので、祖先の
上限は再帰の上限でもある、と考えられていた。
破綻するのはこの「でもある」の部分である。包含関係は構文木ではなくバイト範囲から計算 される。ある定義が別の定義を包含するのは、前者の範囲が後者の開始より先に終わらないとき。 範囲が自分自身より先に終わることはないので、同じバイトを覆う 2 つの定義は「一方が 他方を包含する」と記録される。こうしてできる鎖の長さは重複の個数そのものであり、その環は 1 つも構文木の祖先を増やさないので、スコープの上限には永久に見えない。
焼き込みの Rust grammar はこの形を作らない。3 リリースのあいだ気付かれなかったのはそのため
である。しかし本プロジェクトが配布している PHP grammar は作る。その tags query は
@definition.field を property_declaration に、@name をその下の property_element ごとに
付ける。tree-sitter-tags はタグをノード単位ではなく名前単位で保持するので、
public $a, $b, $c; は 1 つの範囲を覆う 3 つの定義として届く。1 つの文で 3,000 個の
プロパティを宣言するクラスは 22,923 バイトのファイル — ここでの防御であるはずの 1 MiB の
生バイト上限の 2% — であり、これを索引すると実行は STATUS_STACK_OVERFLOW で終わった
。
スタックオーバーフローは panic ではない。 読めない 1 ファイルを実行全体の中止ではなく
skip で済ませているガードはこれを見ることができず、何も書かれず、MCP transport 経由では
プロセスもろともサーバが落ちる。
本決定が答える問い: 2 つの定義が同じバイトを覆うとき、定義とは何か。
emit_def は chunker で唯一の再帰なので、それを縛るものは入力が膨らませられないもので
なければならない選択肢 1。 既に記録された範囲と同じ範囲を覆う定義は、包含関係の木を作る前に落とす。 最も左の名前がその宣言を代表する。
選択肢 2 を採らない理由。 包含深さの上限はクラッシュを止めるが、何が誤りなのかを名指さ ない。同じバイトを覆う定義は深く入れ子になっているのではなく、複数の名前の下に見えている 1 つの定義であり、その重複を入れ子として記録していること自体が欠陥である。しかもこの上限 は、ファイル内のたった 1 つの宣言がたまたま横に長かったという理由で、そのファイルの定義 メタデータを全部失わせる。カンマ区切りのプロパティ列は病的な入力ではなくありふれた PHP である。degrade は「その形が定義単位 chunk 化を破るファイル」への正しい答えであって、 「chunker が単に読み違えたファイル」への答えではない。
選択肢 3 を採らない理由。 重複が消えれば包含関係は構文木より深く入れ子にならず、
スコープの上限が既に自分の限界を超えたものを拒否している。第 3 の上限にはどの入力も到達
しないので、それを出荷することは 3 番目の判断軸の失敗を意図的に出荷することになる。代わり
にこの不変条件は debug_assert! として書いた。発火し得ない場所で強制を主張することなく、
同じことを述べる。
畳み込みはファイルだけから決まるので、同じファイルはどこでも同じように chunk 化され続ける。 宣言全体が chunk の予算に収まる場合、出力は一切変わらない — 鎖の最も外側の定義が既に その宣言を代表しており、最も外側の定義は元から最初の名前だったからである。
symbol_kind にもならないが、chunk の本文には残るので、その名前で検索すれば宣言に届くemit_def を縛っているものが、それに依存している場所で確立されるようになった。
別の量の上限から推論するのをやめた。1 つの宣言に 20,000 個のプロパティを持つファイル
(168,923 バイト) は 629 ms で索引される
groove doctor が警告
すべき損傷ではなく、groove index --force で消える見た目の差である。最も大きく違ったはず
のファイルは実行を終わらせていたファイルであり、それらはそもそも索引されていないgrooveseek/src/parser/code/。畳み込みと包含関係の木は mod.rs にある