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; loadsapp_settingson startup.SiteConfigclass. ~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 viadatabase_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 overcontent_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 legacytask_executor.py). Claims a pendingpipeline_tasksrow, runs it throughcontent_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_blogon prod).run()prefers the DB-storedgraph_def(compiled bypipeline_architect.build_graph_from_spec) whenpipeline_use_graph_def=true— the prod default since #355 — else the legacy PythonTEMPLATESfactory. Drivescanonical_blog(18-node graph_def) +dev_diary(4-node in-tree factory; the hand-codedcanonical_blogfactory was deleted). Postgres checkpointer enabled viatemplate_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 toTemplateRunner.run(template_slug, context)keyed onpipeline_tasks.template_slug. The legacy chunkedStageRunnerflow +plugins/stage_runner.pywere deleted 2026-05-16 (Lane C Stage 4); a NULLtemplate_slugnow 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 deletedtask_executor.pyduring Stage 4. Called bypost_pipeline_actions._maybe_auto_publish.integrations/operator_notify.py—notify_operator(message, critical=...)is the operator-facing entry point. The legacy_notify_discorddirect-webhook path was inlined as_legacy_discord_webhookwhentask_executor.pywas deleted in Stage 4. Alertmanager dispatch (inintegrations/handlers/webhook_alertmanager.py) callsnotify_operatordirectly.
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 LangGraphgenerate_contentnode in thecanonical_blogtemplate. 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. Producesquality_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. Queriescost_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_performanceetc. 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 forcontent_revisionstable. 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 underservices/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)).
| # | File | Purpose |
|---|---|---|
| 1 | verify_task.py | Confirm task exists; catch invalid task_id early. |
| 2 | generate_content.py | Core generation: model selection, RAG context, LLM call, title dedup, scrubbing. |
| 2A.5 | writer_self_review.py | LLM self-review for claim conflicts and revision. |
| 2B | quality_evaluation.py | Fast pattern-based QA; records scores + truncation detection. |
| 2B.1 | url_validation.py | Extract and HTTP-HEAD-verify URLs; flags broken links (non-fatal). |
| 2C | replace_inline_images.py | Find [IMAGE-N] placeholders and inject rendered images (SDXL primary, Pexels fallback). |
| 3 | source_featured_image.py | Featured image via SDXL editorial illustration or Pexels photo. R2 upload with local fallback. |
| 3.5–3.7 | atoms/qa_*.py | Multi-model QA as composable rail atoms (qa.critic / qa.deepeval / qa.guardrails / qa.ragas → qa.aggregate) delegating to multi_model_qa.py. qa.aggregate halts on reject — no rewrite loop. Replaced the deleted cross_model_qa.py (#355). |
| 4 | generate_seo_metadata.py | SEO title / description / keywords from draft. Hard caps (60 / 160 chars). |
| 4B | generate_media_scripts.py | Podcast script + video scenes. Non-critical — failures don’t block publish. |
| 6 | capture_training_data.py | Write draft + score into quality_evaluations + training tables. |
| 7 | finalize_task.py | Persist content_tasks row; sets status=awaiting_approval. Publishing is a separate stage. |
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_configvia DI. Post-Phase-H (GH#95), services do NOT importservices.site_config.site_configat module scope. They accept asite_configkwarg / constructor argument. - All async. Blocking work runs in
asyncio.to_threador 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 byflows/content_generation.py._notify_discordmoved tointegrations/operator_notify.py;_notify_alertmoved intointegrations/handlers/webhook_alertmanager.py;_auto_publish_task/_get_auto_publish_thresholdmoved toauto_publish.py.model_router.py/usage_tracker.py/model_constants.py— the legacy LLM-router trio. Deleted 2026-05-08 (Phase 2 cleanup). Replaced bycost_lookup.py(LiteLLM-backed cost lookup) +llm_providers/dispatcher.py::resolve_tier_model(cost-tier API). Operators tune viaapp_settings.cost_tier.<tier>.modelrows.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 bytemplate_runner.py(LangGraph TemplateRunner).experiment_service.py— A/B harness. Deleted 2026-05-10 (closes Glad-Labs/poindexter#202). Replaced bylangfuse_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.pyis now a thin TemplateRunner dispatcher.
ls src/cofounder_agent/services/. For the load-bearing subset that
drives the critical execution path, see
CLAUDE.md’s “Key services” table.