Skip to content

fix(warehouse-sources): make github workflow_runs webhook-only - #70634

Merged
webjunkie merged 9 commits into
masterfrom
fix/eng-analytics-github-runs-webhook-only
Jul 14, 2026
Merged

fix(warehouse-sources): make github workflow_runs webhook-only#70634
webjunkie merged 9 commits into
masterfrom
fix/eng-analytics-github-runs-webhook-only

Conversation

@webjunkie

@webjunkie webjunkie commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Problem

Connecting a GitHub warehouse source kicks off a full historical crawl of /actions/runs for workflow_runs,
even though the source is meant to run on webhooks.
On a busy repo that is tens of thousands of pages against a shared, rate-limited GitHub App budget,
and webhook mode only switches on once that initial sync finishes.
So the table can take hours to appear, and the crawl pressures every other consumer sharing the installation.

Changes

Make workflow_runs webhook-first, matching workflow_jobs and reviews. Two parts:

  • Set initial_lookback_days=0 on the workflow_runs endpoint config. This marks the schema webhook_only (source.py) and activates webhook mode from the first sync (skips the initial_sync_complete gate in github.py).
  • Make the poll path yield no rows for webhook-only zero-lookback endpoints when webhook mode is inactive. initial_lookback_days only bounds the fan-out children, not the direct workflow_runs poll, so without this the fallback (get_rows) still crawled all of /actions/runs whenever webhook mode was off (no webhook function, or reset_pipeline). Now github_source returns no rows in that case, same as the Slack/Stripe webhook-only sources.

Note

Behavior change for all GitHub sources: new connects no longer backfill workflow_runs history.
They get runs from webhook time onward, same as workflow_jobs today, and supports_poll is false for the endpoint by design.
Existing sources that already completed their backfill are in webhook steady-state and unaffected.
History, if wanted, is a deliberate one-off backfill.

How did you test this code?

I (Claude) did not run a live sync. I added a regression test (test_github_webhook_only.py) that, across all three webhook-only endpoints (workflow_runs, workflow_jobs, reviews), asserts the poll yields zero rows and makes no _fetch_page calls when webhook mode is inactive — locking in that the fallback can't re-crawl history. hogli test on that file passes (3/3), and ruff + hogli ci:preflight are clean.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Julian spotted a freshly connected source doing a long, egress-throttled workflow_runs backfill while its child workflow_jobs (webhook-only) was not.
The first cut was just initial_lookback_days=0, but review (Codex, Greptile, Copilot) correctly pointed out that marker only flips webhook activation — the direct workflow_runs poll ignores it, so the fallback could still crawl full history, and the original comment overstated the effect.
I (Claude) expanded the fix to make the poll fallback yield no rows for webhook-only endpoints (the pattern Slack/Stripe already use), reworded the comment, and added the regression test.
No skills invoked beyond reading the code paths; the change is source-config plus a source-layer poll short-circuit, not a DRF or migration surface.

workflow_runs lacked the initial_lookback_days=0 floor that workflow_jobs and
reviews use, so a freshly connected source crawls the repo's entire
/actions/runs history before webhook mode can engage. On a busy repo that is a
huge, rate-limited backfill against a shared GitHub App budget, and it delays
the table (and anything reading it) for hours.

Set the zero-day floor so the poll does no historical backfill and webhook mode
activates from the first run, matching the sibling webhook-fed endpoints.
@webjunkie webjunkie self-assigned this Jul 14, 2026
@webjunkie
webjunkie marked this pull request as ready for review July 14, 2026 08:02
Copilot AI review requested due to automatic review settings July 14, 2026 08:02
@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 14, 2026 08:03
@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "fix(warehouse-sources): make github work..." | Re-trigger Greptile

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes the GitHub warehouse source’s workflow_runs endpoint behave as webhook-only on initial connect, avoiding an expensive historical crawl of /actions/runs before webhook ingestion can begin.

Changes:

  • Set initial_lookback_days=0 on the workflow_runs endpoint config to align it with other webhook-driven endpoints.
  • Add inline rationale explaining why historical backfill is undesirable for workflow_runs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

@trunk-io

trunk-io Bot commented Jul 14, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@webjunkie webjunkie added the stamphog Request AI approval (no full review) label Jul 14, 2026
@webjunkie
webjunkie enabled auto-merge (squash) July 14, 2026 08:12
@stamphog

stamphog Bot commented Jul 14, 2026

Copy link
Copy Markdown

Note

🤖 stamphog reviewed 654e45cbd90bafd7a558d4e692077ad301597690 — verdict: REFUSED

Verified in source (source.py's source_for_pipeline + webhook_s3.py's webhook_enabled): the new "yield no rows" short-circuit fires for any workflow_runs schema just because the endpoint is webhook-capable, not because that schema is actually configured for webhook sync (schema.is_webhook). Existing production sources with workflow_runs still set to incremental/append poll mode (which was the only option before this PR, per the old comment/test this diff replaces) will silently stop receiving new rows after this ships — exactly the P1 regression Codex flagged, which the author's reply ("we only want schemas to ever be webhook based for runs") doesn't actually refute or fix.

  • Author wrote 100% of the modified lines and has 8 merged PRs in these paths (familiarity STRONG).
  • 👍 on the PR from chatgpt-codex-connector[bot], greptile-apps[bot], hex-security-app[bot].
  • github.py's new empty-iterator short-circuit checks only webhook_source_manager is not None and skip_initial_sync_complete_check — it should also require schema.is_webhook (or equivalent) so existing poll/incremental-mode workflow_runs schemas keep polling instead of silently freezing.
  • Codex's P1 comment on github.py ('Don't no-op existing poll-mode workflow_runs schemas') was marked resolved but the author's reply is an intent statement, not a technical fix — the underlying mechanism it flagged is still present in the current diff.
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list no deny categories matched
size 27L, 3F substantive, 87L/5F incl. docs/generated/snapshots — within ceiling
tier T1-agent / T1b-small (87L, 5F, single-area, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ 5f70f74 · reviewed head 654e45c

Updated in place — this replaces 2 earlier stamphog review(s) on this PR.

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Jul 14, 2026
@webjunkie
webjunkie removed the request for review from a team July 14, 2026 08:15
The initial_lookback_days=0 marker alone only flips webhook activation and
marks the schema webhook_only — it does not bound the direct workflow_runs
poll. If the poll path is ever taken (webhook function missing/disabled, or
reset_pipeline forced it), get_rows still crawled the entire /actions/runs
history, the exact backfill the marker is meant to prevent.

Mirror the Slack/Stripe webhook-only sources: when a webhook-capable,
zero-lookback endpoint has webhook mode inactive, yield no rows instead of
polling. Also reword the settings comment to describe the marker accurately
(it is not a poll floor) and add a regression test across the three
webhook-only endpoints.
chatgpt-codex-connector[bot]

This comment was marked as outdated.

SourceResponse.items returns Iterable | AsyncIterable; list() needs the sync
branch. Assert isinstance Iterable to narrow (and to pin that the webhook-only
fallback returns a sync empty iterator, not the async webhook stream).
@webjunkie webjunkie added the stamphog Request AI approval (no full review) label Jul 14, 2026
@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Jul 14, 2026
@webjunkie
webjunkie requested a review from a team July 14, 2026 09:05
Two pre-existing e2e tests encoded the old "workflow_runs keeps poll" behavior
and failed once workflow_runs became webhook-only:
- test_skip_check_scoped_to_zero_backfill_endpoint expected workflow_runs to
  keep the initial_sync_complete gate; it now skips it like the other
  webhook-only endpoints.
- test_workflow_jobs_is_webhook_only_but_workflow_runs_keeps_poll asserted
  workflow_runs stays poll-backed; both runs and jobs are webhook-only now.

Update the assertions, case names, and the stale source.py comment to match the
intended behavior.
@webjunkie webjunkie added the stamphog Request AI approval (no full review) label Jul 14, 2026
@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Jul 14, 2026
@webjunkie
webjunkie disabled auto-merge July 14, 2026 09:35
…hook

The empty-iterator short-circuit fired for any webhook-capable workflow_runs
schema, so an existing schema still configured for incremental/poll sync (the
only mode before this change) would silently stop receiving rows once
workflow_runs went webhook-only.

Add WebhookSourceManager.schema_is_webhook and only yield no rows when the
schema is actually in webhook mode; legacy poll-mode schemas keep polling. The
extra read only runs on the poll fallback (webhook mode inactive). Add a
regression test for the poll-mode path.
@webjunkie
webjunkie enabled auto-merge (squash) July 14, 2026 09:54
chatgpt-codex-connector[bot]

This comment was marked as outdated.

Same Iterable | AsyncIterable union as the webhook-only case — the poll path
returns a sync iterator, so assert isinstance Iterable before list().
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Backend coverage — 32.0% of changed backend lines covered — 34 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ██████░░░░░░░░░░░░░░ 32.0% (16 / 50)

File Patch Uncovered changed lines
products/warehouse_sources/backend/temporal/data_imports/tests/e2e/github/test_github_source.py 20.0% 560, 1440–1442
products/warehouse_sources/backend/temporal/data_imports/sources/github/tests/test_github_webhook_only.py 21.6% 10–12, 17–19, 21–22, 30, 32–33, 35, 37–39, 46–48, 50–52, 54–55, 63–65, 67, 69–70
products/warehouse_sources/backend/temporal/data_imports/sources/github/github.py 75.0% 975

🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 29330440980 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
warehouse_sources ████████░░░░░░░░░░░░ 42.4% 94,160 / 221,866

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

@webjunkie
webjunkie disabled auto-merge July 14, 2026 10:32
#70640 (now in master) renamed the webhook-first flag to webhook_only; the
poll-noop gate still referenced the old skip_initial_sync_complete_check name.
Point it at the merged webhook_only variable.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b62d68864

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# makes the poll path yield no rows instead of crawling the full /actions/runs history.
# Crawling a busy repo's whole run history on connect is huge against a shared, rate-limited
# budget. History, if wanted, is a deliberate one-off backfill.
initial_lookback_days=0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't skip resets for legacy poll-mode runs

For existing GitHub sources whose workflow_runs schema is still incremental/append (schema.is_webhook is false), this zero-lookback marker makes github_source return SourceResponse.webhook_only=True; the pipeline passes that into handle_reset_or_full_refresh, whose reset_pipeline && webhook_only branch clears reset_pipeline without calling reset_table. Those legacy schemas still poll because of the new schema_is_webhook guard, so a user/admin reset now silently leaves the old Delta table/cursor in place instead of rebuilding. Gate the reset-preservation path on the DB schema actually being webhook sync, not only the endpoint config.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest push. SourceResponse.webhook_only is now derived from the endpoint being webhook-first AND the schema actually being in webhook sync mode (webhook_enabled implies it; otherwise a schema_is_webhook DB check on the poll fallback). So a legacy poll-mode workflow_runs schema reports webhook_only=False and its resets still wipe+rebuild, while it keeps polling. Added assertions in both directions.

…a-aware

The endpoint-level zero-lookback marker flagged SourceResponse.webhook_only=True
even for a legacy poll-mode workflow_runs schema (is_webhook False). That schema
keeps polling (via the schema_is_webhook guard), but the reset-preservation path
in handle_reset_or_full_refresh would then skip the wipe on a user/admin reset,
leaving stale data. Derive webhook_only from the endpoint AND the schema's actual
sync mode, so legacy poll schemas keep wiping+rebuilding on reset. Assert both
directions in the test.
@webjunkie
webjunkie merged commit 7eb6e39 into master Jul 14, 2026
211 of 212 checks passed
@webjunkie
webjunkie deleted the fix/eng-analytics-github-runs-webhook-only branch July 14, 2026 12:19
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 14, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-14 12:53 UTC Run
prod-us ✅ Deployed 2026-07-14 13:06 UTC Run
prod-eu ✅ Deployed 2026-07-14 13:08 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants