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) seedocs/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 acontent_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 modeTWO_PASS, batch size 5, cadence floor 60 minutes - 5 weighted goals —
AUTHORITY35,EDUCATION25,BRAND20,TRAFFIC15,REVENUE5 - 5 enabled sources —
internal_rag50,hackernews20,devto15,web_search10,knowledge5
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 sameTopicBatchService.run_sweepthat 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_sweepjob — fires on the scheduler’s default cadence (every 30 minutes, gated per-niche bydiscovery_cadence_minute_floor). Default for hands-off operation. - Idle worker auto-trigger — the legacy
IdleWorkerbackground loop fires periodically (subject to the niche’sdiscovery_cadence_minute_floorand thetopic_discovery_auto_enabledmaster switch — see kill-switch section). - Manual one-shot trigger — set
topic_discovery_manual_trigger=trueviapoindexter settings set; the next idle worker tick sees it and fires regardless of the auto switch.
2. View the open batch
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
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
niche_carry_forward_decay_factor (default 0.7).
The 4 writer RAG modes
Set per-niche onniches.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
Thetopic_discovery_auto_enabled setting (seeded on by default):
- Default
true— the legacyIdleWorkerdiscovery 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 throughpoindexter topics.
false.
Tuning knobs you’ll actually touch
Most of these live inapp_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:
UPDATE niches SET ... WHERE slug=...
until the niche edit CLI lands — except the cadence target, which has its own
command:
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-sourceCLI. New niches and goal/source edits go in via SQL or via theservices.niche_service.NicheServicePython API. The CLI subgroup exposesniche list,niche show, andniche 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. (Thecost_tier.*tier ladder that might once have backed this was removed in PR #1907; a per-mode pin would be the replacement.) niche_goal_descriptionsis a single global JSON blob in app_settings. Per-niche goal prompt overrides need aniche_goal_promptstable 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.