Skip to main content

Niche Topic Discovery — Operator Guide

Day-to-day operator instructions for the niche-aware topic discovery + RAG writer modes shipped on 2026-04-30 (Glad-Labs/poindexter#278). For the architecture rationale (why this exists, table layout, decay math, provenance trail) see docs/architecture/niches-and-rag-modes.md. This page is the action surface — what to type, when to type it.

What this is, in 3 sentences

Topic discovery now runs per niche. You configure a niche once, then operate it as a stream of 5-candidate batches. The operator picks a winner from each batch; that handoff creates a content_task that flows through the regular pipeline.

Day-zero setup

The Glad Labs niche is pre-seeded (now folded into the baseline migration):
  • slug glad-labs, writer mode TWO_PASS, batch size 5, cadence floor 60 minutes
  • 5 weighted goals — AUTHORITY 35, EDUCATION 25, BRAND 20, TRAFFIC 15, REVENUE 5
  • 5 enabled sources — internal_rag 50, hackernews 20, devto 15, web_search 10, knowledge 5
Confirm it’s there:
Adding a new niche today is a manual process: there is no poindexter niche create CLI yet (see “What’s not yet built” below). Until that lands, insert via SQL or call services.niche_service.NicheService from a Python shell:

The daily workflow

1. Trigger a sweep

Sweeps run one of four ways:
  • poindexter topics sweep --niche <slug> — fires an immediate sweep on demand. Calls the same TopicBatchService.run_sweep that the scheduler uses, so cadence-floor + open-batch checks still apply. Prints the new batch id + candidate count or the reason a sweep was skipped. This is the recommended interactive path (Glad-Labs/poindexter#349).
  • Scheduled run_niche_topic_sweep job — fires on the scheduler’s default cadence (every 30 minutes, gated per-niche by discovery_cadence_minute_floor). Default for hands-off operation.
  • Idle worker auto-trigger — the legacy IdleWorker background loop fires periodically (subject to the niche’s discovery_cadence_minute_floor and the topic_discovery_auto_enabled master switch — see kill-switch section).
  • Manual one-shot trigger — set topic_discovery_manual_trigger=true via poindexter settings set; the next idle worker tick sees it and fires regardless of the auto switch.

2. View the open batch

You’ll see candidate IDs, kinds (external / internal), effective scores, and titles.

3. Rank candidates

Pick your preferred order, best-first. The operator rank is the source of truth — it does not have to match the system rank.

4. (Optional) Edit the winner’s title or angle

5. Resolve — push the winner into the pipeline

This marks the batch resolved, advances operator_rank=1 to a content_task, and unblocks the next sweep for that niche (subject to the cadence floor).

6. Or — reject everything and ask for a fresh sweep

Unpicked candidates carry forward into the next batch with their score multiplied by niche_carry_forward_decay_factor (default 0.7).

The 4 writer RAG modes

Set per-niche on niches.writer_rag_mode. Tasks without a writer_rag_mode fall back to the legacy generator, so pre-niche pipelines still work. TWO_PASS revision loop is hard-capped at writer_rag_two_pass_max_revision_loops (default 3), and each external research call is capped at writer_rag_two_pass_research_max_sources (default 2 sources per [EXTERNAL_NEEDED] marker). All four modes use pipeline_writer_model for the LLM call (no per-mode model override yet — see “What’s not yet built”).

The kill-switch for legacy auto-discovery

The topic_discovery_auto_enabled setting (seeded on by default):
  • Default true — the legacy IdleWorker discovery loop keeps firing on its old signals (queue-low, stale-content, rejection-streak, 24h safety net). OSS installs without configured niches keep their existing behavior.
  • Set false — all auto-firing branches bail out early with an INFO log. Manual triggers still work. Use this for any install where you want to drive everything through poindexter topics.
Flip it:
Glad Labs operators running niches should set this to false.

Tuning knobs you’ll actually touch

Most of these live in app_settings and are seeded with sane defaults. The two niches table columns at the top are per-niche, not app_setting. Set any app_setting via:
Per-niche columns currently require a SQL UPDATE niches SET ... WHERE slug=... until the niche edit CLI lands — except the cadence target, which has its own command:
Sets niches.cadence_target_posts_per_day — a per-niche override that probe_cadence_slo (brain/health_probes.py) checks in addition to the site-wide cadence_slo_expected_posts_per_day target. A niche without this set isn’t checked individually; it’s only covered by the site-wide check.

Troubleshooting

What’s not yet built

Honest gap list as of 2026-04-30:
  • No poindexter niche create / edit / set-goal / enable-source CLI. New niches and goal/source edits go in via SQL or via the services.niche_service.NicheService Python API. The CLI subgroup exposes niche list, niche show, and niche set-cadence — everything else still needs SQL.
  • Writer-mode model selection is hardcoded to pipeline_writer_model. Every niche writer mode uses the one writer pin — there’s no per-mode “a smaller model for the STORY_SPINE outline, a heavier one for the TWO_PASS revise” knob yet. (The cost_tier.* tier ladder that might once have backed this was removed in PR #1907; a per-mode pin would be the replacement.)
  • niche_goal_descriptions is a single global JSON blob in app_settings. Per-niche goal prompt overrides need a niche_goal_prompts table keyed by (niche_id, goal_type). Filed as a follow-up; the JSON shape is the unblock-now compromise.
  • Some commands shown in docs/architecture/niches-and-rag-modes.md (poindexter topics discover, poindexter niche create, poindexter niche set-goal, poindexter niche enable-source) are forward-looking — they describe the intended end state, not the current CLI surface.