fix(pipeline): hold the diff baseline until the state mirror lands [pipeline, index, docs, tests] - #212
Merged
liplus-lin-lay merged 1 commit intoAug 3, 2026
Conversation
…ipeline, index, docs, tests] The metadata-only path in embed-issue advanced its own diff basis — the IssueStore record — before the Vectorize / D1 mirror writes it guards. A mirror write that failed was therefore never retried: the next poll compared GitHub against an already-updated baseline and saw nothing to do. The catch comment promised the next body change would reconcile it, which is true on the embed path and false here, because a state-only change never brings one. Rows indexed while open kept answering searches as live items after the item closed. Two changes at the source, plus a repair for the rows already stranded: - src/pipeline/embed-issue.ts: mirror writes run first, and the IssueStore upsert is skipped when either side failed, so the stale record is what makes the next delivery detect the diff again. The FTS mirror also moves out of the "vector exists" branch — nesting it there dropped the sparse state update for exactly the rows already missing a vector (#210). - src/backfill-issue-state.ts + src/index.ts: POST /admin/backfill-issue-state, a resumable per-repo repair. One paginated state=open listing supplies the truth; indexed open rows absent from it are closed on both sides. No embedding — the dense side re-upserts the existing values with only `state` replaced — so /admin/reset-hashes (full re-embed) is not needed. The backfill aborts rather than act on a truncated open listing, since absence from that listing is what marks a row closed, and it fails before touching D1 if the dense write throws, so a failed call leaves the window unrepaired rather than half-repaired. 恒久修正と backfill を同じ PR に置いたのは、順序を直しても既存の取り残しは 自力で治らないため。差分検出の基準が既に GitHub と一致していて、通常の poll では二度と到達しない。修復方向を open -> closed の一方向に限ったのは、欠陥が 生んだ方向であり、閉じた判断が生きた検討事項として再供給される害の方向でもある から。 Closes #209 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
github-rag-mcp | 560e4c1 | Aug 03 2026, 03:04 AM |
liplus-lin-lay
commented
Aug 3, 2026
liplus-lin-lay
left a comment
Member
Author
There was a problem hiding this comment.
AI self-review (auto mode)
受入基準の充足
issue #209 の修正方針 3 点すべてに対応している。
| 方針 | 結果 |
|---|---|
| 恒久(欠陥 2): mirror の成否が確定するまで差分検出の基準を進めない | 充足。src/pipeline/embed-issue.ts の hash-skip path で IssueStore upsert を mirror 書き込みの後へ移し、いずれかが失敗した場合は基準を進めず failed: true を返す |
| FTS mirror を Vectorize 成功 branch の外へ出す | 充足。dense / sparse を独立して書くようになり、vector 欠落行でも sparse 側の state は更新される |
| backfill を embedding 無しで実装 | 充足。POST /admin/backfill-issue-state。dense は getByIds の既存 values を state だけ差し替えて再 upsert、sparse は UPDATE。embed 呼び出しはゼロ |
CI green(node 164 / workers 58)。docs は同一 PR 内で更新済み(0-requirements 両言語 + installation 両言語 §14)。
diff 実読で確認した点
- cursor の安全性:
selectIndexedOpenRowsはnumber > cursorで再開する。1 issue が複数行に分割されていると最後の番号の残り行を飛ばすが、本番 D1 でCOUNT(*) = COUNT(DISTINCT number) = COUNT(DISTINCT vector_id)(全 repo・全 type で一致)を確認した。issue / PR 行は 1 番号 1 行であり、GitHub の番号空間は issue と PR で共有されるためORDER BY numberは全順序になる。飛ばしは起きない。 - 書き込み順序: dense → sparse。dense が途中で落ちると D1 は未変更のまま呼び出し全体が失敗し、同じ window を再実行できる。両側が割れた状態を残さない。
- 打ち切りガード: 50 ページ超過で何も閉じずに throw。「一覧に無いこと」が close の根拠である以上、部分一覧での判定を拒否するのは正しい。
- vector 欠落行:
mirrorFailedを立てない。#210 側の面であり、embedding 無しでは復旧できないものに基準を人質に取らせない扱いで妥当。 - 返り値の意味変更: metadata mirror 失敗が
skippedUnchangedからfailed: trueになった。poller の stats と webhook レスポンスに現れる。#210 の watermark 修正はこのfailedを未取り込み境界として扱う必要がある(#210 側の要件に含まれている)。
scope の逸脱
無し。範囲外として明示的に残したのは 1 点。
- 逆方向(reopen された項目が
closedのまま残る)を backfill の対象外とした。 欠陥の形は同じだが、恒久修正が今後をカバーする。この判断を実測で裏取りした: 全 6 repo の実 open 項目(liplus-language 33 / webhook-mcp 2 / rag-mcp 4 / dipper_ai 5 / liplus-desktop 5 / neuron-graph-rag 1)に対し、索引側がclosedになっている行は 0 件。既存データに逆方向の取り残しは存在しないため、一方向スコープで欠落は生じない。
次のステップ
auto mode のため human gate 無し。self-review pass → merge。merge + deploy 後に parent が /admin/backfill-issue-state を全 repo に対して実行する。
liplus-lin-lay
deleted the
209-rows-indexed-while-open-keep-a-stale-open-state-after-the-item-is-closed
branch
August 3, 2026 03:08
This was referenced Aug 3, 2026
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.
概要
索引済みの issue / PR 行が、GitHub 側で close された後も
state: "open"のまま残る問題(#209)に対処する。恒久修正(発生源の順序)と backfill(既存の取り残し)を同じ PR に置いた。原因
src/pipeline/embed-issue.tsの hash-skip path は、自分が守っている mirror 書き込み(Vectorize / D1)より先に IssueStore の record を更新していた。この record がmetadataChangedの判定基準なので、mirror が失敗すると次回以降 basis が既に GitHub と一致し、差分が二度と検出されない。catchのコメントが言う「次の body 変更で追いつく」は embed 経路では成り立つが、state だけが変わった行には body 変更が来ないので成り立たない。変更
src/pipeline/embed-issue.ts— mirror 書き込みを先に実行し、どちらかが失敗した場合は IssueStore の upsert をスキップする。stale な record が残ることが、次の poll / webhook 配信で差分を再検出させる仕掛けになる。あわせて FTS mirror を「vector が存在する」branch の外へ出した(内側にネストしていたため、vector が欠けている行では sparse 側の state 更新ごと落ちていた。vector 欠落自体は Index covers only about 45 percent of issue and PR history #210 の範囲)。src/backfill-issue-state.ts+src/index.ts—POST /admin/backfill-issue-state?repo=owner/repoを追加。repo ごとに 1 回のstate=open一覧を正とし、そこに無い索引済みopen行を dense / sparse 両側でclosedにする。embedding 呼び出しは無い(dense 側はgetByIdsで取り直した既存 values をそのまま再 upsert し、metadata のstateだけ差し替える)ので従量コストは発生しない。/admin/reset-hashesは full re-embedding を起こすため使えない。0-requirements.md/.ja.mdに mirror 失敗時の扱い(embed 経路 = best-effort / metadata のみの経路 = 基準を保持)と修復 endpoint、installation.md/.ja.mdに運用手順を追記。安全側の設計
cursorで再実行できる。open->closedの一方向のみ。欠陥が生んだ方向であり、走査対象が索引全体でなく open 集合の大きさに比例する。テスト
src/pipeline/embed-issue.test.ts(新規)— mirror 成功時に基準が進むこと、sparse / dense いずれの失敗でも基準を保持して次回再試行できること、vector 欠落時も sparse 側は更新されること。src/backfill-issue-state.test.ts(新規)— open 集合のページング、truncation ガード、stale 判定、dry run、cursor 再開、Vectorize batch、vector 欠落、dense 失敗時に D1 が無傷であること。src/backfill-issue-state.workers.test.ts(新規)— 実 D1 + migration に対して SELECT / UPDATE を実行し、UPDATE trigger 経由でも FTS5 index が壊れず state filter が正しく切り替わることを確認。Closes #209