Skip to main content

Poindexter Services Reference

Last Updated: 2026-05-23
Auto-generated catalog from src/cofounder_agent/services/; last generated 2026-05-23. The directory holds ~159 files spanning the full pipeline + integration surface. This document is a skimmable tour, not an exhaustive enumeration — see CLAUDE.md’s “Key services” table for the load-bearing subset on the critical execution path. If you add or rename a service, update both this file and the CLAUDE.md table in the same PR.
A skimmable catalog of representative services in src/cofounder_agent/services/. Use this when you want to know “what’s responsible for X” without reading the source. Services are grouped by responsibility. Each entry lists the file, a one-line purpose, and the main class or entry-point function. Approximate line counts are shown as rough complexity indicators — not a target; just a hint.

Core

The runtime spine — configuration, DI, DB coordination, logging.
  • site_config.py — DB-first configuration with env-var fallback; loads app_settings on startup. SiteConfig class. ~250 lines.
  • container.py — Dependency-injection registry for service instantiation. ServiceContainer. ~150 lines.
  • database_service.py — PostgreSQL coordinator orchestrating the six specialized DB modules below. Supports dual pools (cloud + local). ~300 lines.
  • logger_config.py — Structured logging via structlog. get_logger(). ~120 lines.

Database modules

Each module wraps a subset of DB tables and exposes typed CRUD + query helpers. All are reached via database_service.DatabaseService — callers don’t import these directly.
  • admin_database.py — Admin ops: audit trails, financial tracking, agent status, settings. AdminDatabase. ~400 lines.
  • content_database.py — Posts, quality evaluations, categories, tags, authors. ContentDatabase. ~350 lines.
  • tasks_database.py — Task CRUD, status filters, pagination, date-range queries. TasksDatabase. ~280 lines.
  • writing_style_database.py — Writing-sample CRUD for per-user style matching. WritingStyleDatabase. ~200 lines.
  • embeddings_database.py — Embedding storage + pgvector similarity search. EmbeddingsDatabase. ~220 lines.
  • users_database.py — User CRUD + OAuth account linking. UsersDatabase. ~180 lines.
  • content_task_store.py — Thin CRUD adapter over content_tasks. ContentTaskStore. ~150 lines.

Pipeline orchestration & routing

The pipeline glue — who runs what when.
  • flows/content_generation.py — Prefect-orchestrated content pipeline. Sole task dispatch path as of 2026-05-16 (Stage 4 of #410 deleted the legacy task_executor.py). Claims a pending pipeline_tasks row, runs it through content_router_service.process_content_generation_task, then fires post-pipeline side-effects. Cron / retry / heartbeat / stale-run sweep are Prefect-native. Operator UI at http://localhost:4200.
  • template_runner.py — LangGraph-backed dynamic-pipeline orchestrator. Primary pipeline path (default_template_slug=canonical_blog on prod). run() prefers the DB-stored graph_def (compiled by pipeline_architect.build_graph_from_spec) when pipeline_use_graph_def=true — the prod default since #355 — else the legacy Python TEMPLATES factory. Drives canonical_blog (18-node graph_def) + dev_diary (4-node in-tree factory; the hand-coded canonical_blog factory was deleted). Postgres checkpointer enabled via template_runner_use_postgres_checkpointer=true.
  • content_router_service.py — Thin TemplateRunner dispatcher. Builds the shared pipeline context (image_service, settings, style tracker, site_config, models_by_phase, experiment assignment) and hands it to TemplateRunner.run(template_slug, context) keyed on pipeline_tasks.template_slug. The legacy chunked StageRunner flow + plugins/stage_runner.py were deleted 2026-05-16 (Lane C Stage 4); a NULL template_slug now fails loud.
  • enhanced_status_change_service.py — Validates state transitions and logs status changes. EnhancedStatusChangeService. ~180 lines.
  • handle_task_status_change.py — Async handler for status-change events emitted by the dispatcher. ~120 lines.
  • auto_publish.py — Auto-publish helpers (auto_publish_task + get_auto_publish_threshold). Ported from the deleted task_executor.py during Stage 4. Called by post_pipeline_actions._maybe_auto_publish.
  • integrations/operator_notify.pynotify_operator(message, critical=...) is the operator-facing entry point. The legacy _notify_discord direct-webhook path was inlined as _legacy_discord_webhook when task_executor.py was deleted in Stage 4. Alertmanager dispatch (in integrations/handlers/webhook_alertmanager.py) calls notify_operator directly.

LLM & inference

  • ollama_client.py — Local LLM inference via Ollama with cost tracking (GPU watts × duration × rate). OllamaClient. ~400 lines.
  • ai_content_generator.py — Used by the LangGraph generate_content node in the canonical_blog template. Composes the writer prompt, runs the LLM call, and returns the draft for downstream stages. AIContentGenerator. ~350 lines.
  • gpu_scheduler.py — Serializes GPU access between Ollama and SDXL. Yields to gaming workloads automatically. ~100 lines.

Image generation & selection

  • image_service.py — Post-Phase-G dispatcher for SDXL + Pexels. Unified interface. ImageService. ~250 lines.
  • image_generation_config.py — SDXL model registry and prompt-template selection. ~150 lines.
  • image_selection_service.py — Strategy router: SDXL vs Pexels based on availability + failure cascades. ~200 lines.
  • image_prompt_builder.py — Builds editorial SDXL prompts from topic / tags / style. ~180 lines.
  • image_generation_runner.py — Executes the SDXL render; R2 upload with local fallback. Path-traversal guards. ~120 lines.

Content quality & validation (QA stack)

  • content_validator.py — Deterministic programmatic quality gate. Catches fake people, fake stats, unlinked citations, hallucinated libraries. validate_content(). ~1,200 lines. The highest-leverage file in the repo.
  • quality_evaluation.py — Fast pattern-based QA. Produces quality_score, truncation_detected, findings. ~200 lines.
  • citation_verifier.py — HTTP-HEAD dead-link detection. Advisory, never blocks publish alone. ~120 lines.
  • multi_model_qa.py — Multi-model critic + gate reviewers (consistency, url_verifier, web_factcheck, vision_gate). Aggregates weighted scores. ~1,600 lines.
  • quality_checker.py — Legacy QA evaluation abstraction. PATTERN_BASED + LLM modes. ~100 lines.

Content processing & SEO

  • excerpt_generator.py — 1–2 sentence summaries for index / RSS / social. generate_excerpt(). ~90 lines.
  • alt_text.py — Alt-text sanitation + budget-aware summarization. strip_pipeline_tokens(), sanitize_alt_text(). ~110 lines.
  • html_sanitizer.py — HTML cleaning with safe-tag filtering. ~130 lines.
  • slugify_service.py — URL-safe slug generation. Dupe-prevention. ~80 lines.
  • category_resolver.py — Maps topic keywords to category UUIDs via taxonomy. ~110 lines.

Media & transcription

  • media_script_generator.py — Podcast script + video scene generation from draft. Two separate LLM calls for reliability. ~180 lines.
  • transcription_service.py — Audio-to-text via external provider. ~120 lines.

Cost, analytics & observability

  • cost_aggregation_service.py — Cost analytics by phase / model / provider. Queries cost_logs. CostAggregationService. ~200 lines.
  • audit_log.py — Fire-and-forget audit logger. Non-blocking background writes. AuditLogger, audit_log_bg(). ~140 lines.

Caching & performance

  • redis_cache.py — Async Redis cache with TTL + health checks. RedisCache. ~220 lines.
  • decorators.py@log_query_performance etc. Slow-query thresholds configurable. ~190 lines.

Search & embeddings

  • embedding_service.py — Orchestrates embedding generation, storage, deduplication. Uses the LLM Provider Protocol. ~180 lines.
  • rag_embeddings_service.py — Vector context retrieval for RAG prompts. ~140 lines.
  • vector_similarity_search.py — Cosine-distance queries over pgvector. ~100 lines.

Decision logging (ML loop)

  • decision_service.py — Standard interface for ML decision logging + outcome tracking. log_decision(), record_outcome(). ~140 lines.
  • stateless_decision_handler.py — Ephemeral decision computations; no persistence. ~100 lines.

Integration

  • devto_service.py — Dev.to cross-posting with canonical URL. DevToCrossPostService. ~140 lines.
  • notifications_service.py — User / system notification dispatch. ~130 lines.

Utilities & helpers

  • error_handler.py — Domain exception hierarchy: AppError, ValidationError, NotFoundError, DatabaseError. ~100 lines.
  • paging_helpers.py — Offset/limit utilities for list queries. ~90 lines.
  • database_mixin.py — Shared row-to-dict / type-coercion helpers. DatabaseServiceMixin. ~80 lines.
  • bootstrap_defaults.py — Centralized localhost / fallback URLs. ~60 lines.
  • default_author.py — Gets or creates the “Poindexter AI” author row. get_or_create_default_author(). ~50 lines.
  • content_revisions_logger.py — Write-through helper for content_revisions table. Tracks QA iteration history. log_revision(). ~70 lines.
  • idle_worker.py — Background maintenance when the pipeline is idle. Cleanup, optimization, scheduled tasks. IdleWorker. ~120 lines.

Pipeline stages (12)

Stages live under services/stages/ and are wired into the LangGraph templates in services/pipeline_templates/__init__.py. TemplateRunner orchestrates them. Each implements the Stage protocol (name, run(context)).
#FilePurpose
1verify_task.pyConfirm task exists; catch invalid task_id early.
2generate_content.pyCore generation: model selection, RAG context, LLM call, title dedup, scrubbing.
2A.5writer_self_review.pyLLM self-review for claim conflicts and revision.
2Bquality_evaluation.pyFast pattern-based QA; records scores + truncation detection.
2B.1url_validation.pyExtract and HTTP-HEAD-verify URLs; flags broken links (non-fatal).
2Creplace_inline_images.pyFind [IMAGE-N] placeholders and inject rendered images (SDXL primary, Pexels fallback).
3source_featured_image.pyFeatured image via SDXL editorial illustration or Pexels photo. R2 upload with local fallback.
3.5–3.7atoms/qa_*.pyMulti-model QA as composable rail atoms (qa.critic / qa.deepeval / qa.guardrails / qa.ragasqa.aggregate) delegating to multi_model_qa.py. qa.aggregate halts on reject — no rewrite loop. Replaced the deleted cross_model_qa.py (#355).
4generate_seo_metadata.pySEO title / description / keywords from draft. Hard caps (60 / 160 chars).
4Bgenerate_media_scripts.pyPodcast script + video scenes. Non-critical — failures don’t block publish.
6capture_training_data.pyWrite draft + score into quality_evaluations + training tables.
7finalize_task.pyPersist content_tasks row; sets status=awaiting_approval. Publishing is a separate stage.
See architecture/services/template_runner.md for the full pipeline narrative and architecture/plugin-architecture.md for the Stage protocol itself.

Conventions

  • All services take site_config via DI. Post-Phase-H (GH#95), services do NOT import services.site_config.site_config at module scope. They accept a site_config kwarg / constructor argument.
  • All async. Blocking work runs in asyncio.to_thread or a worker queue.
  • Errors are typed via error_handler.py. Services raise domain exceptions; routes translate them to HTTP status codes.
  • Decisions are logged via decision_service.log_decision() for the ML loop.
  • Never import from stages/*. Stages are wired into LangGraph templates; they don’t call each other. Cross-stage data flows through the pipeline context dict.

What’s NOT in this catalog (deleted services)

The following modules are referenced in older docs / git history but no longer exist on disk. Listed here so a grep doesn’t send you on a wild goose chase:
  • task_executor.py — the legacy polling daemon. Deleted 2026-05-16 in Prefect Stage 4 (Glad-Labs/poindexter#410). Replaced by flows/content_generation.py. _notify_discord moved to integrations/operator_notify.py; _notify_alert moved into integrations/handlers/webhook_alertmanager.py; _auto_publish_task / _get_auto_publish_threshold moved to auto_publish.py.
  • model_router.py / usage_tracker.py / model_constants.py — the legacy LLM-router trio. Deleted 2026-05-08 (Phase 2 cleanup). Replaced by cost_lookup.py (LiteLLM-backed cost lookup) + llm_providers/dispatcher.py::resolve_tier_model (cost-tier API). Operators tune via app_settings.cost_tier.<tier>.model rows.
  • workflow_executor.py + custom_workflows_service.py + template_execution_service.py + workflow_validator.py + phase_mapper.py + phase_registry.py + workflow_progress_service.py + phases/ + schemas/custom_workflow_schemas.py + agents/ — the workflow-executor chain. Deleted 2026-05-09 (~3,800 LOC). Replaced by template_runner.py (LangGraph TemplateRunner).
  • experiment_service.py — A/B harness. Deleted 2026-05-10 (closes Glad-Labs/poindexter#202). Replaced by langfuse_experiments.py (Langfuse Datasets/Traces/Scores).
  • plugins/stage_runner.py — the legacy chunked StageRunner. Deleted 2026-05-16 in Lane C Stage 4. content_router_service.py is now a thin TemplateRunner dispatcher.
For the directory’s current contents, run ls src/cofounder_agent/services/. For the load-bearing subset that drives the critical execution path, see CLAUDE.md’s “Key services” table.