Skip to main content

Handler: tap.builtin_topic_source

Adapter that brings the existing in-repo topic_source scrapers (hackernews, devto, web_search, knowledge, codebase) into the declarative external_taps model.

What it does

Delegates to services.topic_sources.runner.run_all() (the same runner the scheduler already uses) and filters the summary to the source name specified in row.tap_type. The actual topic persistence (dedup, similarity scoring, content_task row creation) happens inside the existing plugin — this handler is purely a shape adapter.

Row configuration

Seeded rows (all disabled)

The baseline migration seeds one row per current topic_source plugin with sensible schedules:
NameSchedule
hackernewsevery 1 hour
devtoevery 2 hours
web_searchevery 6 hours
knowledgeevery 12 hours
codebaseevery 1 day

Operator runbook

Enabling a seeded topic source

Wait for the next scheduler tick and verify last_run_at populates:

Adding a new built-in source

If you write a new services/topic_sources/*.py plugin and register it under plugins.topic_source, seed it as a tap row:

Disabling

Immediately effective — no restart.

Why this adapter exists

The existing topic_source plugin architecture is already declarative-ish (via plugin.topic_source.<name> app_settings rows). This adapter just brings the enablement into one table (external_taps) so:
  1. Grafana can show tap state alongside webhook / retention state.
  2. Operator CLI is uniform (poindexter taps … mirrors webhooks … and retention …).
  3. Future Singer-protocol taps live in the same table — one list of “what is this system ingesting from?”.

Caveats

  • Full-runner invocation: each handler call invokes run_all() and filters to the tap’s name. When multiple built-in taps are enabled, each fires its own run_all(). The existing runner is idempotent via content_hash dedup, so this is safe but slightly wasteful. Fixing this would require the topic_sources runner to accept a single-source filter — tracked as a follow-up.
  • No per-source config isolation in v1: the old plugin.topic_source.<name>.config app_settings rows still drive per-source behavior. Moving that config into external_taps.config is a follow-up migration.
  • Framework overview: Integrations
  • Sibling handler: tap.singer_subprocess (stub — full external Singer tap support)
  • Existing runner: services/topic_sources/runner.py
  • GH-103 (external taps issue)