目的
索引済みの issue / PR 行のうち、GitHub 側では close 済みなのに search_docs.state が open のまま残っている行を実態へ揃える。あわせて、同じ取り残しが再発する経路を閉じる。
確認済みの事実(2026-08-03 実測)
D1 search_docs と GitHub 実物の突き合わせ:
repo
索引 open (issue / PR)
実 open (issue / PR)
取り残し
Liplus-Project/liplus-language
165 / 114
33 / 0
132 / 114
Liplus-Project/github-rag-mcp
10 / 12
0 / 0
10 / 12
Liplus-Project/github-webhook-mcp
12 / 9
2 / 0
10 / 9
境界が一点にそろっている。 liplus-language の issue 側の取り残しは #1027〜#1604 に限られ、#1605 以降はゼロ(SQL で確定: state='open' かつ実 open 集合に無い行の MAX(number) = 1604)。
ずれる条件は「索引した後に close された」こと。 反証として #600〜#630 を実物と全件突き合わせたところ、索引にある 23 件は type・state とも全て正しかった。close 済みの状態で索引された行は正しく、open の状態で索引され後から close された行だけがずれる。
原因(2026-08-03 実装から確定)
state 反映を担う経路は src/pipeline/embed-issue.ts の hash-skip path。body hash が一致した場合に metadata 差分を検出し、Vectorize metadata と search_docs を更新する(FTS mirror は #86 / 2026-04-19 から在る)。ここに 2 つの欠陥が重なっている。
欠陥 1 — mirror 書き込みが例外で落ちていた(#175 で解消済み)
search_docs への書き込みは src/fts.ts upsertFtsRow の ON CONFLICT (vector_id) DO UPDATE。#175 (migration 0005)以前は search_docs の UPDATE trigger が反対側 tokenizer index へ存在しない row の delete command を送っており、既存行への UPDATE が例外で落ちていた。例外は catch (ftsErr) に「非致命・次の body 変更で追いつく」として飲まれる。
境界の裏取り(#175 の deploy 時刻と一致):
事象
時刻 (JST)
取り残しに含まれるか
#175 merge (d40898e)
2026-07-29 12:11
—
liplus-language #1604 close
2026-07-29 12:18
含まれる(取り残し最大番号)
liplus-language #1610 close
2026-07-29 13:58
含まれない
merge 直後の数分(deploy 完了前)に close された #1604 までが取り残し、deploy 後に close された #1610 は正常。#1605 は現在も open なので取り残しではなく正しい open。→ 恒久修正は #175 で既に効いている。 本 issue に残る恒久側の作業は欠陥 2 のみ。
欠陥 2 — 失敗した mirror が二度と再試行されない
差分検出の基準は IssueStore (Durable Object) の既存 record であり、これは mirror 書き込みより 前 に更新される(hash-skip path は先に POST /upsert を打つ)。一度 IssueStore が closed になると次回以降 metadataChanged が false になり、mirror は永久に再試行されない。catch のコメントが言う「次の body 変更で追いつく」は state だけが変わった行には成り立たない(body は変わらないので二度と来ない)。
副次: FTS mirror の呼び出しが vectors.length > 0 && vectors[0].values の内側にネストしているため、vector が欠けている行では sparse 側の state 更新ごとスキップされる(vector 欠落自体は #210 の範囲)。
修正方針
恒久(欠陥 2) : mirror 書き込みの成否が確定するまで差分検出の基準を進めない。あるいは mirror 失敗を記録して次回 poll で再試行させる。現状の「基準を先に進める」順序が再試行を殺している。あわせて FTS mirror を Vectorize 成功 branch の外へ出す。
backfill(既存の取り残し行) : embedding 呼び出しは不要 と確定した。state は GitHub の list API から取れ、search_docs.state は UPDATE、Vectorize 側は getByIds で既存 values を取り直して metadata だけ差し替えた再 upsert で足りる。よって従量コストは発生せず、go-sign 待ちの対象外。
vector が存在しない行は Index covers only about 45 percent of issue and PR history #210 の欠落側なので本 issue では触らない。
制約
対象ファイル
src/pipeline/embed-issue.ts(hash-skip path の順序と mirror のネスト)
src/index.ts(backfill admin endpoint の追加)
docs/0-requirements.md / docs/0-requirements.ja.md
回帰テスト
背景
Li+ 側の過去判断照会(skills/evolution-judgment-learning)で RAG を引いた際、2026-05-13 に close 済みの liplus-language #1283 が open として返り発覚した。閉じた設計判断が「まだ生きている検討事項」として後続の検索へ再供給される経路になる。
目的
索引済みの issue / PR 行のうち、GitHub 側では close 済みなのに
search_docs.stateがopenのまま残っている行を実態へ揃える。あわせて、同じ取り残しが再発する経路を閉じる。確認済みの事実(2026-08-03 実測)
D1
search_docsと GitHub 実物の突き合わせ:境界が一点にそろっている。 liplus-language の issue 側の取り残しは
#1027〜#1604に限られ、#1605以降はゼロ(SQL で確定:state='open'かつ実 open 集合に無い行のMAX(number)= 1604)。ずれる条件は「索引した後に close された」こと。 反証として
#600〜#630を実物と全件突き合わせたところ、索引にある 23 件は type・state とも全て正しかった。close 済みの状態で索引された行は正しく、open の状態で索引され後から close された行だけがずれる。原因(2026-08-03 実装から確定)
state 反映を担う経路は
src/pipeline/embed-issue.tsの hash-skip path。body hash が一致した場合に metadata 差分を検出し、Vectorize metadata とsearch_docsを更新する(FTS mirror は #86 / 2026-04-19 から在る)。ここに 2 つの欠陥が重なっている。欠陥 1 — mirror 書き込みが例外で落ちていた(#175 で解消済み)
search_docsへの書き込みはsrc/fts.tsupsertFtsRowのON CONFLICT (vector_id) DO UPDATE。#175(migration 0005)以前はsearch_docsの UPDATE trigger が反対側 tokenizer index へ存在しない row の delete command を送っており、既存行への UPDATE が例外で落ちていた。例外はcatch (ftsErr)に「非致命・次の body 変更で追いつく」として飲まれる。境界の裏取り(#175 の deploy 時刻と一致):
d40898e)merge 直後の数分(deploy 完了前)に close された #1604 までが取り残し、deploy 後に close された #1610 は正常。
#1605は現在も open なので取り残しではなく正しいopen。→ 恒久修正は #175 で既に効いている。 本 issue に残る恒久側の作業は欠陥 2 のみ。欠陥 2 — 失敗した mirror が二度と再試行されない
差分検出の基準は IssueStore (Durable Object) の既存 record であり、これは mirror 書き込みより 前 に更新される(hash-skip path は先に
POST /upsertを打つ)。一度 IssueStore がclosedになると次回以降metadataChangedが false になり、mirror は永久に再試行されない。catchのコメントが言う「次の body 変更で追いつく」は state だけが変わった行には成り立たない(body は変わらないので二度と来ない)。副次: FTS mirror の呼び出しが
vectors.length > 0 && vectors[0].valuesの内側にネストしているため、vector が欠けている行では sparse 側の state 更新ごとスキップされる(vector 欠落自体は #210 の範囲)。修正方針
search_docs.stateは UPDATE、Vectorize 側はgetByIdsで既存 values を取り直して metadata だけ差し替えた再 upsert で足りる。よって従量コストは発生せず、go-sign 待ちの対象外。制約
/admin/reset-hashesは full re-embedding を起こすので使えない)。対象ファイル
src/pipeline/embed-issue.ts(hash-skip path の順序と mirror のネスト)src/index.ts(backfill admin endpoint の追加)docs/0-requirements.md/docs/0-requirements.ja.md背景
Li+ 側の過去判断照会(
skills/evolution-judgment-learning)で RAG を引いた際、2026-05-13 に close 済みの liplus-language#1283がopenとして返り発覚した。閉じた設計判断が「まだ生きている検討事項」として後続の検索へ再供給される経路になる。