perf(crawlers): GraphQL metadata batch + tree-call cache (rate-limit ⑤+⑥b) - #64
Merged
Conversation
find_skill_md_paths_cached skips the recursive Trees API call when a
repo's pushed_at is unchanged since it was last walked, reusing the
cached {path: blob_sha} map. Wired through the topic crawler. Makes a
warm run approach zero metered calls per unchanged repo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…on 4) In incremental/discover mode, append pushed:>last-run to each topic search query so re-runs only surface repos changed since the previous crawl, instead of re-paginating every query in full every time. Last-run timestamp is persisted via crawl_state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…k I/O run() now always touches crawl_state; mock load/save_crawl_state in the existing run tests so they no longer write data/crawl_state/topic.json. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fetch_repo_metadata_batch fetches stars/pushedAt/defaultBranch/topics for up to 100 repos in one GraphQL POST (separate 5k-point/hr pool) instead of one REST call each. The topic crawler bulk-fetches discovered repos and falls back to the per-repo cached REST path for any repo GraphQL omits. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A discovery that returns no repos (transient rate-limit / empty search) must not push last_discovery_at forward, or the next incremental run would silently skip repos changed in the gap. Guard the crawl_state save on a non-empty discovery; add a regression test. Also tightens the discovery-state test to a positive assertion and removes unused mocks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n clean sweep Codex review (P2 x2): (1) the GraphQL metadata batch fetched every discovered repo before the already-covered/filter-cache skips ran, wasting quota in CI (--data-dir data/raw skips most). Batch only the to-process set. (2) advancing last_discovery_at on any non-empty discovery could skip repos left unprocessed by a --limit truncation or a per-repo failure; advance only after a complete, clean sweep. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codex review (P2): _discover_topic_repos swallows a per-query search RuntimeError and returns a partial list; advancing last_discovery_at then skips repos from the failed query next run. _discover_topic_repos now returns (repos, discovery_complete) and the watermark only advances when discovery completed cleanly (in addition to the existing not-truncated/not-failed guards). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codex review: (P1) discover mode date-filters discovery to only new repos but left resume=False, so write_jsonl truncated the existing topic corpus to the new subset — enable resume/append for discover like incremental. (P2) the GraphQL metadata batch eagerly fetched every to_process repo before the --limit check; fetch lazily in chunks of 100 as the loop consumes repos. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sweep codex review: (P1) hitting the 1000-repo discovery cap left discovery_complete=True, so a partial capped discovery advanced the watermark and skipped repos beyond the cap — mark a capped result incomplete. (P3) a *complete* discovery that finds nothing new now advances the watermark (the discovery_complete flag already gates transient failures), so quiet periods don't re-scan the same window forever. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rite
codex review: (P2) find_skill_md_paths_cached cached a {} result, but
find_skill_md_paths returns {} for both "no SKILL.md" and a transient Trees
failure — pinning a repo that has skills to empty forever. Only cache
non-empty results (genuine empties are handled by the filter cache).
(P2) the discovery watermark was saved before write_jsonl; a failed/killed
write would advance past unwritten repos. Save it only after a successful write.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rtial codex review: (P2) a timed-out topic search returns HTTP 200 with incomplete_results=true and a partial page; mark discovery incomplete so the watermark won't advance past the omitted repos. (P2) --limit hit inside a repo's inner SKILL.md loop left truncated=False (only the outer loop set it), so the watermark could advance with that repo's remaining skills unwritten; set truncated in the inner break too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e cache on default branch Option 4's pushed:>since filter is unsound for topic discovery: a repo that adds a skill topic without a new commit keeps its old pushed_at, so it is filtered out and the advancing watermark makes it permanently undiscoverable (GitHub search has no topic-added-since qualifier). Remove the date filter and the whole watermark/crawl_state machinery; topic discovery searches fully each run (cheap via the downstream metadata/tree/content caches, not via narrowed discovery). Keep options 5 (GraphQL batch) and 6b (tree cache), and fix 6b to key on (pushed_at, default_branch) so a default-branch change isn't served stale paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Summary
Implements rate-limit options ⑤ (GraphQL metadata batch) and ⑥b (tree-call cache) from
docs/crawler-rate-limit-strategy.md, building on ①②③ (#63). Reference integration is the topic crawler. Built test-first via subagent-driven development; per-task reviewed plus 8 rounds ofcodex review.find_skill_md_paths_cachedreuses the cached{path: blob_sha}map when a repo's(pushed_at, default_branch)is unchanged, skipping the recursive Trees API call.pushed_atcomes free from the ②-cached metadata. This is what makes a warm run approach zero metered calls per unchanged repo. Keyed ondefault_branchtoo, since a default-branch change re-resolvesHEADto a different tree without bumpingpushed_at; empty/failed lookups are never cached (so a transient Trees failure can't pin a repo to "empty").fetch_repo_metadata_batchfetches stars/pushedAt/defaultBranch/topics for up to 100 repos in one GraphQL POST (separate 5k-point/hr pool) instead of one REST call each. Uses GraphQL variables (no injection), tolerates partialerrors, omits null repos, chunks >100. The topic crawler pre-filters discovered repos through the skip checks, then lazily batches metadata in chunks of 100 as the loop consumes them (so a small--limitdoesn't over-fetch), falling back to the per-repo cached REST path for any repo GraphQL omits.Composition: GraphQL gives
pushed_at+default_branchfor every repo in one call → feeds ⑥b's tree-skip → feeds ③'s content cache.④ was dropped (deliberately)
Option ④ (date-filtered incremental topic discovery via
pushed:>last-run) was implemented, then removed after codex review found it fundamentally unsound: a repo that adds a skill topic without a new commit keeps its oldpushed_at, sopushed:>sincefilters it out and the advancing watermark makes it permanently undiscoverable — and GitHub search has no "topic-added-since" qualifier to fix it. Topic discovery now searches fully each run (correct; repo-search at 30/min was never the bottleneck — the wins are ⑤/⑥b and the ①②③ caches). ④ returns to the backlog noting incremental topic discovery needs a different mechanism (e.g. enumerate-and-diff, not a date watermark). ⑦/⑧ were also deferred (low/niche).Verification
pytest— 571 passed, 20 deselected;ruff check crawlers/ tests/crawlers/clean; tests do no network/disk (verified nodata/crawl_state/topic.jsonleak).--limit; tree-cache hit/miss/stale-pushed_at/changed-default-branch/empty-not-cached.Risk & rollback
Moderate — touches
crawlers/base.py(new helpers, no signature change to existing public fns) and the topic crawler loop. Additive; per-repo REST path remains as fallback. Only the topic crawler is wired. Rollback = revert. No index/runtime/schema change.🤖 Generated with Claude Code