Skip to content

Fix/convex pipeline health - #41

Merged
flvvius merged 6 commits into
mainfrom
fix/convex-pipeline-health
Jul 8, 2026
Merged

Fix/convex pipeline health#41
flvvius merged 6 commits into
mainfrom
fix/convex-pipeline-health

Conversation

@flvvius

@flvvius flvvius commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Feed fetching can now use per-feed timeout settings, improving reliability for slower sources.
    • Summaries are generated sooner after clustering and merge activity, speeding up updates.
  • Bug Fixes

    • Publishing is now held until summaries are successfully generated, reducing premature public updates.
    • Events that can’t qualify for a full summary are automatically moved out of published status to keep public previews accurate.

flvvius and others added 4 commits July 8, 2026 19:43
…es after clustering

Events no longer go public on article/source counts alone. Publishing is now
gated on a successful AI summary: clustering keeps qualifying events in
`processing`, and applyEventSummaryResult flips status -> `published` once an
event has neutral/reformist/suveranist perspectives + globalImpact (and syncs
its public preview). Effective publish bar = the summary bar (3 articles / 2
sources), matching the two thresholds.

Summaries are no longer trickled by the 45-min cron alone: clustering, merge,
and recluster batches now trigger summarizeQueuedEvents immediately, so an event
that just crossed the bar is summarized and published right away. Enqueue
selection scans qualifying `processing` events (primary) plus `published`
(re-summarize/backfill); shouldResummarize drops its published-only gate.
event_summary_batch_size raised 3 -> 8 to keep pace.

Verified with a new convex-test suite (promotion, enqueue eligibility, singleton
skip, no-op re-enqueue) + full backend suite (166 pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rs Agerpres)

Agerpres has no native RSS; it routes through the bazqux createfeed extractor,
which scrapes the Agerpres widget live and routinely takes ~7-20s. The flat 15s
AbortSignal.timeout aborted it most runs (AbortError), quarantining the national
wire service and starving cross-source clustering. Add an optional per-feed
fetchTimeoutMs (default 15s), set Agerpres to 30s, and thread it through
ingestSingleFeed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One-time cleanup for the publish-on-summary switchover. Reverts `published`
events that can never earn an AI summary (below the summary min articles/sources
and lacking one) back to `processing`, deleting their public preview, so the new
"public only with an AI summary" invariant holds. Qualifying published-but-
summary-less events are left for the summary backfill to fill in place. Dry-run
by default; idempotent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@flvvius, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 93230a14-6b3d-496f-8458-a1dae65f621a

📥 Commits

Reviewing files that changed from the base of the PR and between 8668717 and beb331a.

📒 Files selected for processing (8)
  • apps/web/src/components/bookmark-button.tsx
  • apps/web/src/lib/posthog.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/routes/admin.pipeline.tsx
  • apps/web/src/routes/politica-confidentialitate.tsx
  • apps/web/src/routes/source.$sourceId.tsx
  • packages/backend/convex/aiBudget.ts
  • packages/backend/convex/auth.ts

Walkthrough

Publish status is decoupled from clustering thresholds and gated instead on successful AI summarization, with immediate summarization scheduling after clustering/merging, a config default update, a data migration to revert misclassified published events, per-feed fetch timeout support, and a PostHog UI host override.

Changes

Publish-on-summary gating and clustering changes

Layer / File(s) Summary
Remove threshold-based publish status from clustering
packages/backend/convex/clustering.ts
attachArticleToEvent preserves existing event status instead of computing it from shouldPublishCluster, and drops publishMinArticles/publishMinSources args from schema, handler, and caller.
Immediate summarization scheduling after clustering
packages/backend/convex/clustering.ts
mergeNearDuplicateEvents, reclusterRecentSingletonEvents, and clusterEnrichedArticles schedule summarizeQueuedEvents immediately after any merge or attachment.
Summarization eligibility and publish promotion
packages/backend/convex/summarization.ts
shouldResummarize no longer excludes non-published events; enqueueEligibleEventSummaries scans processing and published events together; applyEventSummaryResult promotes processing events to published on success.
Config default updates
packages/backend/convex/config.ts
event_summary_batch_size default raised from 3 to 8; cluster_publish_min_articles/cluster_publish_min_sources descriptions updated to reflect legacy gating role.
Migration to revert unsummarizable published events
packages/backend/convex/migrations.ts
New revertUnsummarizablePublishedEvents migration reverts published events lacking a full AI summary and not meeting thresholds back to processing, syncing public previews, with dryRun and sampling support.
Publish gate test suite
packages/backend/convex/summarizationPublishGate.test.ts
New tests verify processing-to-published promotion, preview creation, and enqueue/skip logic for the publish gate.

Per-feed fetch timeout override

Layer / File(s) Summary
FeedEntry timeout field and ingestion wiring
packages/backend/convex/feeds.ts, packages/backend/convex/ingestion.ts
FeedEntry adds optional fetchTimeoutMs, applied to the Agerpres feed (30s), and threaded through ingestSingleFeed/ingestAllFeeds to override the default RSS fetch abort timeout.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Clustering as clusterEnrichedArticles
  participant Scheduler
  participant SummarizeJob as summarizationNode.summarizeQueuedEvents
  participant Enqueue as enqueueEligibleEventSummaries
  participant Apply as applyEventSummaryResult
  participant Events as events table

  Clustering->>Events: attach articles, preserve event status
  alt articles clustered/attached
    Clustering->>Scheduler: runAfter(0, summarizeQueuedEvents)
    Scheduler->>SummarizeJob: invoke immediately
  end
  SummarizeJob->>Enqueue: scan processing + published events
  Enqueue->>Events: enqueue eligible summary jobs
  Events-->>Apply: job returns AI summary result
  Apply->>Events: patch summary fields
  alt event.status == processing
    Apply->>Events: set status = published
  end
Loading

Possibly related PRs

  • flvvius/news#18: Overlaps on attachArticleToEvent behavior/signature and summarization logic in the same files.
  • flvvius/news#20: Both modify enqueueEligibleEventSummaries eligibility and job-enqueueing logic in summarization.ts.
  • flvvius/news#32: Both change post-clustering scheduling behavior in clustering.ts for merge/recluster flows.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is broadly aligned with the Convex backend pipeline changes, though it’s more general than the specific summary of clustering and summarization fixes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/convex-pipeline-health

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@vercel

vercel Bot commented Jul 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
news Error Error Jul 8, 2026 6:22pm
news-web Ready Ready Preview, Comment Jul 8, 2026 6:22pm

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/backend/convex/ingestion.ts (1)

1622-1635: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Retry path drops fetchTimeoutMs override.

The retry call to ingestSingleFeed at Line 1624 omits fetchTimeoutMs: feed.fetchTimeoutMs, so retried feeds fall back to the 15s default. For the Agerpres feed (30s override), a retry after a timeout will likely time out again, defeating the per-feed override.

🐛 Proposed fix
         const retryResult = await ctx.runAction(
           internal.ingestion.ingestSingleFeed,
           {
             feedUrl: feed.url,
             feedName: feed.name,
             feedDomain: feed.domain,
             baseBias: feed.baseBias,
             reliabilityScore: feed.reliabilityScore,
             mbfcCategory: feed.mbfc.category,
             mbfcFactual: feed.mbfc.factual,
             mbfcCredibility: feed.mbfc.credibility,
+            fetchTimeoutMs: feed.fetchTimeoutMs,
           },
         );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/convex/ingestion.ts` around lines 1622 - 1635, The retry
path in ingestion should preserve the per-feed timeout override when calling
ingestSingleFeed. Update the ctx.runAction payload inside the feedsToRetry loop
to include feed.fetchTimeoutMs alongside the existing
feedUrl/feedName/feedDomain and MBFC fields, so retries honor the same timeout
configuration as the original ingest path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/lib/posthog.tsx`:
- Around line 19-23: The PostHog config in posthog.tsx currently mixes regions
by defaulting api_host to the US endpoint while hardcoding ui_host to the EU
endpoint, so update the PostHogProvider setup to keep ui_host aligned with the
actual project region. Make ui_host configurable via an env var in the same
style as POSTHOG_HOST, and ensure the fallback matches the same region used by
api_host so toolbar and session-recording links resolve to the correct project.

In `@packages/backend/convex/summarizationPublishGate.test.ts`:
- Around line 136-166: Add a complementary test in
summarizationPublishGate.test.ts for enqueueEligibleEventSummaries that covers a
published event without a current AI summary, since the existing test only
verifies the skip path for a published event that already has one. Reuse the
existing test setup around convexTest,
internal.summarization.enqueueEligibleEventSummaries, and the events insert
shape, but omit or change the current AI summary fields so the event should
qualify and be queued; then assert the queued count increases as expected.

---

Outside diff comments:
In `@packages/backend/convex/ingestion.ts`:
- Around line 1622-1635: The retry path in ingestion should preserve the
per-feed timeout override when calling ingestSingleFeed. Update the
ctx.runAction payload inside the feedsToRetry loop to include
feed.fetchTimeoutMs alongside the existing feedUrl/feedName/feedDomain and MBFC
fields, so retries honor the same timeout configuration as the original ingest
path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ba284e93-99c0-490f-a520-7718f980ba94

📥 Commits

Reviewing files that changed from the base of the PR and between 2996c66 and 8668717.

📒 Files selected for processing (8)
  • apps/web/src/lib/posthog.tsx
  • packages/backend/convex/clustering.ts
  • packages/backend/convex/config.ts
  • packages/backend/convex/feeds.ts
  • packages/backend/convex/ingestion.ts
  • packages/backend/convex/migrations.ts
  • packages/backend/convex/summarization.ts
  • packages/backend/convex/summarizationPublishGate.test.ts

Comment thread apps/web/src/lib/posthog.tsx Outdated
Comment on lines +136 to +166
test("a published event with a current AI summary is not re-enqueued", async () => {
const t = convexTest(schema, modules);
const now = Date.now();
await t.run(async (ctx) => {
await ctx.db.insert("events", {
title: "Already Summarized",
slug: "already-summarized",
status: "published",
firstPublishedAt: now,
lastUpdatedAt: now,
lastArticleAt: now,
articleCount: 3,
sourceCount: 2,
sourceIds: [],
perspectiveSummaries: {
neutral: "n",
reformist: "r",
suveranist: "s",
},
perspectiveSource: "ai",
globalImpact: "g",
lastSummarizedAt: now,
});
});

const result = await t.mutation(
internal.summarization.enqueueEligibleEventSummaries,
{ limit: 10, minArticles: 3, minSources: 2 },
);
expect(result.queued).toBe(0);
});

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a test for published events without a current AI summary.

The PR summary states enqueueEligibleEventSummaries now scans both processing and published events. Test 4 only covers a published event that already has a summary (and is correctly skipped). A complementary test for a published event without a current AI summary — which should be enqueued for summarization — would verify the new behavior that enables the migration's "backfill in place" strategy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/backend/convex/summarizationPublishGate.test.ts` around lines 136 -
166, Add a complementary test in summarizationPublishGate.test.ts for
enqueueEligibleEventSummaries that covers a published event without a current AI
summary, since the existing test only verifies the skip path for a published
event that already has one. Reuse the existing test setup around convexTest,
internal.summarization.enqueueEligibleEventSummaries, and the events insert
shape, but omit or change the current AI summary fields so the event should
qualify and be queued; then assert the queued count increases as expected.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- posthog.tsx: repair botched ui_host splice (stray duplicate posthog.init +
  const wedged inside the object literal); hoist POSTHOG_UI_HOST to module scope.
- bookmark-button.tsx: type useMutation via FunctionReturnType/FunctionArgs so
  onSuccess data is known (was inferred as {}).
- source.$sourceId.tsx: pin `data` to the query return type so mapped `article`
  isn't implicitly any (loaderData is untyped through the router context).
- __root.tsx: @ts-expect-error the root beforeLoad return-context (TanStack
  Router v1.132 types it as `never`; returning context is the correct runtime
  contract read via useRouteContext).
- Remove dead code / unused symbols: admin.pipeline pct(), politica Link import,
  aiBudget startOfUtcDay(), auth sendAuthEmail's unused actionUrl binding.

Web app + backend both typecheck clean; backend suite 166 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@flvvius
flvvius merged commit 6328b85 into main Jul 8, 2026
3 of 4 checks passed
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.

1 participant