Report unmatched repo filter in search response - #220
Merged
Conversation
search の repo フィルタが1件もマッチしなかった場合を、レスポンスの filters_unmatched フィールドで明示する。repo はフルスラッグ (owner/repo) の 完全一致なので、短いリポジトリ名を渡すと候補集合が空のまま「該当なし」と 同じ形のレスポンスが返り、呼び出し側からフィルタ不成立とヒットゼロが 区別できなかった。多段のエージェンティック検索ではゼロが正常な中間結果と して消費されるため、この silent zero は偽陰性のまま流れてクエリ予算だけを 焼く。 変更点: - fts.ts: repoHasIndexedRows (search_docs への LIMIT 1 存在確認) と detectUnmatchedFilters (候補ゼロのときだけプローブ/プローブ失敗時は 観測していない不成立を主張しない) を追加 - mcp.ts: search モードのレスポンスに filters_unmatched を常時付与し、 tool schema の repo description に完全一致であることを明記 - mcp-server/server/tools.js: 静的スキーマのミラーを同期 - テスト: 「フィルタ不成立」と「ヒットゼロ」が別レスポンスになることを node 側 (判定ロジック) と workers 側 (実 D1 の SQL) の両面で検証 - docs / README (ja/en): filters_unmatched の意味と非スコープを記載 検索ロジック (fusion / rerank / 候補数) は一切変更していない。観測性のみ。 短いスラッグからフルスラッグへの自動解決は非スコープ。 Refs #219 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
github-rag-mcp | a6e1305 | Aug 06 2026, 04:37 AM |
liplus-lin-lay
commented
Aug 6, 2026
liplus-lin-lay
left a comment
Member
Author
There was a problem hiding this comment.
セルフレビュー結果: pass
受入基準チェック
| issue #219 の要求 | 実装 | 判定 |
|---|---|---|
| フィルタ不成立を結果ゼロと区別できる形で返す | filters_unmatched を search モードのレスポンスに常設、[] が「全フィルタ成立」 |
満たす |
| 検索ロジックは変更しない | fusion / rerank / 候補数に変更なし。追加はプローブ1本とレスポンスの1フィールド | 満たす |
最小スコープは repo 1本 |
repo のみプローブ。理由(もっともらしい誤値が存在する唯一のフィルタ)をコードコメントに明記 |
満たす |
| 短縮名→フルスラッグの自動解決はしない | 非スコープとして維持、コメントで理由を明示 | 満たす |
| テスト1本(不成立とヒットゼロが別レスポンス) | 3層(決定ロジック node / 実 D1 workers / proxy スキーマ)で実装 | 満たす |
設計の検証
- プローブが hot path に乗らない:
candidateCount === 0のときだけ実行。候補が1件でもあればフィルタ成立の証拠なので追加の読みが発生しない。非対称の根拠がコメントに書かれている。 - プローブ失敗を所見にしない: catch で握って空リストを返す。観測していない不成立を主張しない安全側。
- 候補数の取り方: dense(Vectorize metadata
$eq)と sparse(d.repo = ?)の両方が同じ完全一致なので、repo誤値は両側を同時に空にする。合計を条件にするのが正しい。 - proxy ミラーの同期:
mcp-server/server/tools.jsはクライアントがtools/listで実際に読む記述面。ここを同期しないと警告が届かない。同期済みで、スキーマテストも追加されている。
スコープ逸脱
なし。対象ファイル外への波及は proxy ミラーとドキュメント4本のみで、いずれも issue の対象ファイル列挙に含まれる面。
持ち越し
scan モード(query 省略)に同じ silent zero が残る。マージ前に実測で確認し、#221 として起票済み。
repo |
mode | count | filters_unmatched |
|---|---|---|---|
liplus-language(短縮) |
scan | 0 | フィールド自体が無い |
Liplus-Project/liplus-language |
scan | 3 | フィールド自体が無い |
PR #220 がこれを除外した判断は妥当。scan は Durable Object の recency endpoint から集約するため母集合が異なり、search_docs へのプローブでは答えられない。抜けではなく別作業。
次の期待
auto モードにつき人間ゲートなし。リリース種別 patch(既存フィールドの意味を変えず追加のみ)。self-review pass によりマージへ進む。
liplus-lin-lay
deleted the
219-search-returns-a-silent-zero-when-the-repo-filter-matches-nothing-indistinguishable-from-no-hits
branch
August 6, 2026 04:40
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.
Closes #219
目的
searchのrepoフィルタが1件もマッチしなかった場合を、結果ゼロと区別できる形でレスポンスに明示する。検索ロジックは一切変更せず、観測性のみを足す。何が問題だったか
repoはフルスラッグ (owner/repo) の完全一致である。dense 側は Vectorize metadata の$eq、sparse 側はd.repo = ?。短いリポジトリ名を渡すと両方の候補集合が空になり、本当にヒットが無かった場合とまったく同じ形のレスポンスが返る。単発検索ならゼロは行き止まりとして目に付くが、エージェンティックな多段検索ではゼロは「この角度には何も無かった」という正常な中間結果として消費される。フィルタを疑う契機がループに吸収され、偽陰性のままクエリ予算だけを1回分焼く。
実装
src/fts.tsrepoHasIndexedRows—SELECT 1 FROM search_docs WHERE repo = ? LIMIT 1。repoは既存の index (idx_search_docs_repo) が効き、FTS5 は経由しない。detectUnmatchedFilters— 判定本体。候補集合が空のときだけプローブする (候補が1件でもあればフィルタ成立の証拠なので、追加の読みが hot path に乗らない)。プローブ自体が失敗したら「観測していない不成立」は主張せず空を返す。src/mcp.ts— search モードのレスポンスにfilters_unmatchedを常時付与 ([]は全フィルタ成立 =count: 0が真のヒットゼロ)。tool description とrepoの description に完全一致であることを明記。mcp-server/server/tools.js— 静的スキーマのミラーを同期。クライアントが実際に読む description はこちらなので、完全一致の注意はここに載らないと届かない。filters_unmatchedの意味、プローブ条件、非スコープを記載。プローブ対象は
repoのみ。もっともらしく見える誤値 (フルスラッグに対する短いリポジトリ名) が存在するのはこのフィルタで、state/typeは enum 制約があり、milestone/assigneeに同種の near-miss 形はない。テスト
「フィルタ不成立」と「ヒットゼロ」が別のレスポンスになることを二面で検証:
src/fts.test.ts(node) — 判定ロジック。誤スラッグ→["repo"]/ 実在 repo のヒットゼロ→[]/ 候補ありならプローブしない / repo フィルタ無しならプローブしない / プローブ失敗時は何も報告しない。src/fts.workers.test.ts(実 D1) — SQL。同じ0件でも誤スラッグと無関係クエリはプローブで分かれること、および完全一致 (前方・後方一致しない) の確認。mcp-server/test/search-tool-schema.test.js— proxy スキーマのrepodescription が完全一致要件とfilters_unmatchedに言及していること。ローカルで
npm test(node 213 + workers 60)、npx tsc --noEmit、node scripts/check-schema-drift.mjs、npx wrangler deploy --dry-runすべて green。非スコープ
search_docsとは母集合が別なので、同じプローブでは答えられない。リリース種別
patch。既存フィールドの意味を変えず追加のみで、検索結果そのものは変わらない。
🤖 Generated with Claude Code