Skip to Content
AgentsDeliverable PlannerDeliverable Planner — Architecture

Deliverable Planner — Architecture

The Deliverable Planner sits between the strategy layer and the production pipeline. It answers one question: given what the strategy says we want to achieve, what should we actually produce each month, on which channels, and how much of it? Its output is the standing monthly plan — a stable set of commitments that persists until the admin refreshes it.


System Context — Where It Fits

┌─────────────────────────────────────────────────────────────────────────┐ │ STRATEGY LAYER │ │ │ │ Client Researcher ──► Competitor Researcher ──► Strategy Writer │ │ │ │ │ Approved strategy doc │ │ (Markdown, ~3,000 words) │ └──────────────────────────────────────────────────┬──────────────────────┘ │ human approves strategy ┌────────────────────────────────────┐ │ DELIVERABLE PLANNER │ ← This component │ agent__deliverable-planner │ │ Claude Sonnet 4.6 │ └────────────────────┬───────────────┘ │ produces Goals + DeliverablePlan │ human approves plan ┌────────────────────────────────────┐ │ ACTIVITY PLANNER │ │ Converts plan → monthly pipeline │ └────────────────────────────────────┘ ┌────────────────────────────────────┐ │ 25 WORKER AGENTS │ │ blog-writer, gbp-post-writer, … │ └────────────────────────────────────┘

Component Diagram

╔═════════════════════════════════════════════╗ TRIGGERS ║ DELIVERABLE PLANNER ║ OUTPUTS ║ ║ Strategy Writer ──► 1. Receive job ║──────► PostgreSQL approval event ║ (tenantId, approvedStrategy, ║ goals table (auto-trigger) ║ planType, connectedChannels, ║ (one row per goal) ║ budgetHint?, monthlyHours?) ║ Dashboard API ────► ║──────► PostgreSQL ("Refresh Plan" ║ 2. RAG: uploaded scope docs ║ deliverable_templates button) ║ rag_search → Client Documents ║ (one row per type) ║ "scope of work", "content volume" ║ ║ ║──────► HITL gate ║ 3. Filter by plan + channels ║ (Dashboard → ║ Remove unavailable deliverable types ║ Strategy → ║ ║ Deliverable Plan) ║ 4. LLM planning call ║ ║ Claude Sonnet 4.6 ║ ║ → DeliverablePlannerOutput JSON ║ ║ ║ ║ 5. Validate + cost-check ║ ║ Every goal linked to ≥1 deliverable ║ ║ Total cost ≤ plan credit limit ║ ║ ║ ║ 6. Persist + create approval record ║ ╚═════════════════════════════════════════════╝ DATA SOURCES ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────────┐ │ PostgreSQL │ │ MongoDB │ │ Qdrant │ │ │ │ │ │ │ │ tenant_settings │ │ skills: │ │ ds_cd_{tenant} │ │ agent_configs │ │ client-context- │ │ Client Documents │ │ plan entitle- │ │ file.md │ │ "scope of work" │ │ ments │ │ deliverable- │ │ "content volume" │ │ │ │ types.md │ │ "publishing sched." │ └──────────────────┘ └──────────────────┘ └──────────────────────┘

Data Flow — Full Sequence

┌─────────────────────────────────────────────────────────────────────────┐ │ Step 1 — TRIGGER │ │ │ │ Source A — Auto-trigger: │ │ Admin approves the Strategy Writer output in Dashboard │ │ → platform emits strategy_approved event │ │ → BullMQ enqueues job to agent__deliverable-planner │ │ Payload: { tenantId, approvedStrategy (Markdown), planType, │ │ connectedChannels, budgetHint?, monthlyHours? } │ │ │ │ Source B — Manual refresh: │ │ Admin clicks "Refresh Plan" in Dashboard → Strategy │ │ → same payload + existingDeliverables[] for diff comparison │ └─────────────────────────────────────────┬───────────────────────────────┘ ┌─────────────────────────────────────────▼───────────────────────────────┐ │ Step 2 — CONTEXT LOAD │ │ │ │ PostgreSQL ──► tenant_settings (industry, plan tier, channels) │ │ PostgreSQL ──► agent_configs (model, skills, RAG datasets) │ │ MongoDB ──► client-context-file.md │ │ MongoDB ──► deliverable-types.md (canonical type list) │ │ Qdrant ──► Client Documents: │ │ "deliverable targets", "scope of work", │ │ "content volume", "publishing schedule" │ └─────────────────────────────────────────┬───────────────────────────────┘ ┌─────────────────────────────────────────▼───────────────────────────────┐ │ Step 3 — PLAN & CHANNEL FILTERING │ │ │ │ Available deliverable types = canonical list │ │ MINUS types requiring plan tier > tenant's current plan │ │ MINUS types requiring channels not in connectedChannels │ │ │ │ Example — Free plan + only Google Business Profile connected: │ │ Available: blog_post, gbp_post, keyword_cluster, content_brief, │ │ social_post, social_calendar, email_newsletter │ │ Excluded (no channel): social_post, social_calendar (no social │ │ channel connected), email_newsletter (no email connected) │ │ Excluded (Pro+ only): google_ads_copy, meta_ads_copy, │ │ landing_page, monthly_report │ └─────────────────────────────────────────┬───────────────────────────────┘ ┌─────────────────────────────────────────▼───────────────────────────────┐ │ Step 4 — PROMPT ASSEMBLY + LLM CALL │ │ │ │ buildActivityPrompt() substitutes placeholders: │ │ {{TENANT_SETTINGS}} ← PostgreSQL │ │ {{PLAN_TYPE}} ← job payload │ │ {{CONNECTED_CHANNELS}} ← job payload │ │ {{MONTHLY_HOURS}} ← job payload (optional) │ │ {{BUDGET_HINT}} ← job payload (optional) │ │ {{APPROVED_STRATEGY}} ← job payload (full Markdown) │ │ {{CLIENT_CONTEXT}} ← MongoDB skill │ │ {{RAG_CONTEXT}} ← Qdrant results │ │ {{AVAILABLE_DELIVERABLE_TYPES}} ← filtered type list │ │ │ │ Claude subprocess: │ │ claude -p "..." --add-dir /tmp/skills-{uuid}/ │ │ --output-format stream-json --model claude-sonnet-4-6 │ │ │ │ Tool calls: rag_search × 1 only (Client Documents) │ │ No web tools — pure planning from injected context │ │ │ │ Returns: DeliverablePlannerOutput JSON │ │ { goals[], deliverables[], monthlyCostEstimate, planningNotes } │ └─────────────────────────────────────────┬───────────────────────────────┘ ┌─────────────────────────────────────────▼───────────────────────────────┐ │ Step 5 — VALIDATION │ │ │ │ ✓ deliverables[] has ≥ 1 entry │ │ ✓ Every strategy goal linked to ≥ 1 deliverable type │ │ ✓ No monthlyVolume exceeds the max in deliverable-types matrix │ │ ✓ All agentQueue values match canonical queue names │ │ ✓ No deliverable types outside the filtered available set │ │ ✓ monthlyCostEstimate ≤ plan credit limit (re-prompt if over) │ │ └─ if still over after retry → HITL warning, require override │ └─────────────────────────────────────────┬───────────────────────────────┘ ┌─────────────────────────────────────────▼───────────────────────────────┐ │ Step 6 — PERSIST │ │ │ │ PostgreSQL INSERT → goals (one row per GoalRecord) │ │ PostgreSQL INSERT → deliverable_templates (one row per DeliverableSpec)│ │ PostgreSQL INSERT → agent_runs (via publishAgentEvent) │ │ agent:started → row created at job begin │ │ agent:completed / agent:failed → row updated with duration + cost │ │ PostgreSQL INSERT → deliverable_plan_logs (plan_generated event) │ │ Notification sent → tenant admin (in-app + email) │ │ │ │ If refresh: previous deliverablePlan archived (status: archived); │ │ planVersion incremented on new record │ └─────────────────────────────────────────────────────────────────────────┘

HITL Gate — Deliverable Plan Review

┌─────────────────────────────────────────────────────────────────────────┐ │ Dashboard → Strategy → Deliverable Plan │ │ │ │ Admin sees: │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Deliverable Plan — April 2026 │ │ │ │ │ │ │ │ Goals │ │ │ │ • Grow organic traffic +40% (6 months) → blog + GBP │ │ │ │ • 25 additional enquiries/month → blog + Google Ads │ │ │ │ │ │ │ │ Monthly Deliverables Credits/mo │ │ │ │ ✓ SEO Blog Post 4/month [ 4 ] ± 8 cr │ │ │ │ ✓ GBP Post 8/month [ 8 ] ± 8 cr │ │ │ │ ✓ Google Ads RSA 1/month [ 1 ] ± 2 cr │ │ │ │ ○ Social Post — disabled │ │ │ │ │ │ │ │ Estimated monthly cost: 18 credits of 30 available │ │ │ │ │ │ │ │ [Edit volumes] [Approve — Start producing] │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ On approve: │ │ approvals record → status: approved │ │ Event fired → Activity Planner enqueued for this month │ └─────────────────────────────────────────────────────────────────────────┘

Upstream: Strategy Layer

The Deliverable Planner does not decide what strategy to pursue. That decision lives one level up.

┌───────────────────────────────────────────────────────┐ │ Client Researcher │ │ Produces: market snapshot, audience profile │ └───────────────────────────┬───────────────────────────┘ ┌───────────────────────────▼───────────────────────────┐ │ Competitor Researcher │ │ Produces: competitor analysis, content gaps │ └───────────────────────────┬───────────────────────────┘ ┌───────────────────────────▼───────────────────────────┐ │ Strategy Writer │ │ Produces: full strategy doc with SMART goals, │ │ channel priorities, content angles │ │ Human approves this document │ └───────────────────────────┬───────────────────────────┘ │ approval event ┌───────────────────────────────────────────────────────┐ │ DELIVERABLE PLANNER │ │ Reads: strategy goals, channel priorities │ │ Decides: what types, what volumes, what channels │ │ Produces: goals[], deliverable_templates[] │ │ Human approves this plan │ └───────────────────────────┬───────────────────────────┘ │ approval event ┌───────────────────────────────────────────────────────┐ │ ACTIVITY PLANNER │ │ Reads: deliverable_templates (what + volumes) │ │ Decides: which topics, which weeks, which sequence │ │ Produces: activities[], BullMQ jobs │ └───────────────────────────────────────────────────────┘

Plan Tier Filtering

The deliverable type list is filtered at runtime based on the tenant’s plan. This ensures the output only contains types the tenant can actually produce.

Free / Starter / Growth plan: ✓ blog_post ✓ gbp_post ✓ keyword_cluster ✓ content_brief ✓ social_post ✓ social_calendar ✓ email_newsletter ✗ landing_page (Pro+) ✗ google_ads_copy (Pro+) ✗ meta_ads_copy (Pro+) ✗ monthly_report (Pro+) Pro / Professional plan: ✓ All of the above + landing_page, google_ads_copy, meta_ads_copy, monthly_report Agency / Enterprise: ✓ All Pro + higher volume caps + custom deliverable types

Refresh Flow

When the admin clicks “Refresh Plan” (e.g. after a strategy update, a plan upgrade, or after connecting a new channel):

existingDeliverables[] passed in job payload Model produces new DeliverablePlannerOutput changesSummary computed: + Added: social_post (8/month) — Instagram connected ~ Changed: blog_post 2/month → 4/month (plan upgraded to Pro) - Removed: meta_ads_copy — Meta Ads disconnected Old goals + deliverable_templates archived (not deleted) New records inserted, keyed to new planVersion HITL gate — admin reviews changes before new pipeline begins

Multi-Tenancy

Queues are shared across all tenants — one agent__deliverable-planner queue handles jobs for all tenants. Tenant isolation is enforced via tenantId in the job payload.

agent__deliverable-planner → PostgreSQL: goals (tenant_id from job payload) PostgreSQL: deliverable_templates (tenant_id from job payload)

Concurrency: 5 — lightweight Claude call (~8k tokens in, ~800 out); many tenants can run simultaneously.


Key Numbers

AttributeValue
Queueagent__deliverable-planner (shared, all tenants)
ModelClaude Sonnet 4.6
Concurrency5
Timeout5 min
Avg input tokens~8,000
Avg output tokens~800
Est. cost / run~$0.35
Tool callsrag_search × 1 only — no web tools
PlanFree+ (all tenants)
DownstreamActivity Planner (after human approval)

© 2026 Leadmetrics — Internal use only