fix(poller): hold the issue watermark at the first uningested item [poller, pipeline, index, docs, tests] - #215
Merged
liplus-lin-lay merged 1 commit intoAug 3, 2026
Conversation
…oller, pipeline, index, docs, tests] (#210) pollRepo fetches up to 200 items per run (MAX_PAGES_PER_RUN x 100) and embeds at most 50 (MAX_EMBEDDINGS_PER_RUN). The rest were stored with an empty bodyHash as a retry marker — and then the watermark advanced past them anyway, to the poll start time, or to the last fetched item's updated_at when pagination capped. The batch is sorted `updated` ascending, so both bounds sit above every deferred item: the marker was set and the marked item was never fetched again. Measured 2026-08-03, about 55% of the issue and PR history of the indexed repositories was absent from search_docs, scattered rather than in contiguous ranges because the loss follows updated_at order, not number order. Same defect the commit-diff surface had (#178 / #179), on a surface the pin was never applied to. - src/poller.ts: processIssues reports the first item it left off the retrieval surfaces (budget-deferred or embed-failed) as a retry boundary, and nextIssueWatermark pins the watermark one second before it — the same margin and never-regress shape as nextForwardDiffWatermark, since /issues documents `since` as "updated after" and would otherwise drop the boundary item. The response ETag is withheld on the same condition: storing it would make the next conditional request answer 304 and return before it saw the leftover. - src/backfill-issue-index.ts + src/index.ts: POST /admin/backfill-issue-index, a resumable per-repo sweep over the issue-number space that ingests the numbers with no search_docs issue/PR row. Numeric cursor, because a timestamp cursor over the same set reintroduces the ordering the defect exploited. dry_run measures the gap without spending the embed budget. - src/pipeline/embed-issue.ts: `force` option, set only by the backfill. Every candidate there is known to be missing a retrieval surface, and a matching hash — which an embed whose FTS5 mirror failed leaves behind — would otherwise skip it permanently. 恒久修正と backfill を同じ PR に置いたのは、watermark を直しても既存の欠落は 自力で埋まらないため。取り残された項目が再 fetch されるのは updated_at が動いた ときだけで、閉じた履歴はもう動かない。実行順序は逆にできない: backfill を先に 走らせると、その後に入る新規項目が同じ経路でまた落ちる。release surface の別種の 穴は #211 に分離してある。 Closes #210 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
github-rag-mcp | 17542df | Aug 03 2026, 03:22 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 #210 の修正方針 2 点を満たしている。
| 方針 | 結果 |
|---|---|
恒久: 見送った最初の item の手前で watermark を止める(#179 の pin と同型、since の inclusive 境界に対する backoff margin 込み) |
充足。processIssues が retryBoundary(embedding budget 見送り / embed 失敗の最初の item の updated_at)を返し、nextIssueWatermark が ISSUE_RETRY_BOUNDARY_BACKOFF_MS = 1000 手前へ pin。never-regress と unparseable-date-holds も nextForwardDiffWatermark と同じ形 |
| backfill: repo 指定・cursor・件数上限を取る admin endpoint | 充足。POST /admin/backfill-issue-index。番号空間を歩く(timestamp cursor だと欠陥が突いた順序を持ち込むため)。limit 1..100 既定 25 |
CI green(3 checks)。docs は同一 PR 内(0-requirements 両言語 + installation 両言語 §15)。
diff 実読で確認した点
- ETag の穴(issue 本文に無い、担当が見つけた 2 つ目の欠陥): watermark を止めても ETag を保存すると次 run が 304 で早期 return し、止めた意味が消える。
holdEtag = capped || retryBoundary !== undefinedで塞いである。この修正が実効を持つことを store 側から裏取りした——setWatermarkはetag ?? ""で書き、getWatermarkはetag || undefinedで返すので、withhold は「前の値を残す」ではなくクリアになる。次 run はIf-None-Matchを送らず full fetch する。この確認が取れなければ修正は空振りだった - cursor の正確性:
stopAt = nは n を candidates に入れた後に立ち、nextCursor = scannedTo = n、次回はnumber > n。取りこぼしも二重処理も無い forceの適用範囲: hash 一致でも embed する経路は backfill だけが立てる。poll / webhook 経路は hash check を保つので全件再 embed にはならない。embed 成功 + FTS mirror 失敗の行は hash が残り通常経路では永久にスキップされるため、この option は必要nextIssueWatermarkの never-regress: pin が現在の watermark 以下なら現在値を返す。バッチ先頭が失敗した場合に前進も後退もしない
検証の裏取り
担当が「watermark logic を巻き戻すと新規回帰テスト 4 本が落ちる」ことを実走で確認している。テスト構成を読み、drains a backlog larger than the budget across runs without skipping(130 件 3 run)が欠陥の再現そのものを見張っていることを確認した。
受容したトレードオフ
- 永続的に embed 失敗する item が 1 件あると、その repo の issue 索引全体が止まる。 修正前は追い越して黙って落としていた。これは #178 が明示的に選んだ「silent gap より visible stall」の判断と同じで、commit-diff 面が既に同じ性質を持っている。専用のアラームは無く、毎 run の
watermark=... (pinned before ...)ログが同一値を繰り返すことが停止のシグネチャになる。commit-diff 面と観測性の水準は同じなので、本 PR が新たに作った差分ではない
scope の逸脱
無し。ETag の穴は issue 本文に無いが、恒久修正が実効を持つための必要条件であり同一欠陥の一部。
次のステップ
auto mode のため human gate 無し。self-review pass → merge。
残件(PR 範囲外、parent が deploy 後に実行): /admin/backfill-issue-index を全 6 repo に対して cursor を回して実行する。Master の go-sign 取得済み(2026-08-03、対象 1239 件)。順序は deploy → backfill。逆順だと backfill 後に入る新規分が同じ経路で再び落ちる。
liplus-lin-lay
deleted the
210-index-covers-only-about-45-percent-of-issue-and-pr-history
branch
August 3, 2026 03:25
This was referenced Aug 3, 2026
Closed
Merged
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.
何を直したか
pollRepoは 1 run で最大 200 件(MAX_PAGES_PER_RUN× 100)を fetch する一方、embed するのは最大 50 件(MAX_EMBEDDINGS_PER_RUN)。残りは空のbodyHashを retry の印として保存していたが、watermark はその印を追い越して進んでいた — 完走時は poll 開始時刻、pagination 打ち切り時は最後に fetch した項目のupdated_at。batch はupdated昇順なので、どちらの分岐も見送った項目より必ず新しい位置に着地する。結果、印は付くが印の付いた項目が二度と fetch されない。2026-08-03 の実測で、索引対象 repository の issue / PR 履歴の約 55%(1239 件)が
search_docsに無かった。欠落が連続した番号帯でなく散発に見えるのは、脱落が番号順ではなくupdated_at順に起きるため。commit diff surface で #178 / #179 が既に潰した構造と同型で、issue / PR surface には適用されていなかった。
変更
恒久修正 —
src/poller.tsprocessIssuesが「retrieval surface に載せられなかった最初の項目」(embedding 予算で見送り / embed 失敗)のupdated_atを retry boundary として返す。batch は昇順なので、最初の 1 件を押さえれば以降も全部覆える。nextIssueWatermarkがその 1 秒手前に watermark を留める。margin と never-regress の形はnextForwardDiffWatermarkと同型 —/issuesのsinceは "updated after" と規定されているので、境界の項目自身が次 window から落ちうるため。backfill —
src/backfill-issue-index.ts+src/index.tsPOST /admin/backfill-issue-index?repo=owner/repo[&dry_run=true][&limit=N][&cursor=N]search_docsに issue / PR 行が無い番号を取り込む。番号空間は密かつ有界なので、数値 cursor が「どこまで走査したか」を厳密に表せる。時刻 cursor は使わない — 欠陥が突いた順序をそのまま持ち込むことになるため。limit(既定 25 / 上限 100)で、これは subrequest 予算であると同時に Workers AI の予算。dry_run=trueは予算を使わず欠落量だけを測る(1 call 最大 5000 番)。absentに計上して retry しない。src/pipeline/embed-issue.tsforceoption を追加(backfill だけが指定)。候補はいずれも retrieval surface が欠けていると分かっている項目で、hash 一致(embed 成功後に FTS5 mirror が失敗した行がこの状態)だと恒久的に skip されてしまうため、body-hash 判定を飛ばす。テスト
回帰テスト 4 本は、修正を戻すと 4 本とも落ちることを確認済み:
加えて
nextIssueWatermarkの単体 5 本と、backfill module の 17 本。全体 249 件 green、tsc --noEmitclean。実行順序
恒久修正の deploy → backfill の実行、の順。逆にすると backfill 後に入る新規項目が同じ経路でまた落ちる。backfill の実行は merge + deploy 後に parent が行う(Master の go-sign は 2026-08-03 取得済み)。
release surface の別種の穴は #211 に分離済み。#209(索引済み行の state 取り残し)とは独立。
Closes #210