Activity Planner
[Live] ·
agent__activity-planner· Claude Sonnet 4.6
Reads the current period’s approved deliverable plan and creates the activity pipeline — one task per agent per deliverable unit — by enqueuing BullMQ jobs and writing Activity records to the database. Generates no content itself.
Overview
| Function | Convert the approved deliverable plan into a sequenced activity pipeline for the current period |
| Type | Orchestrator |
| Model | Claude Sonnet 4.6 |
| Queue | agent__activity-planner |
| Concurrency | 1 |
| Timeout | 15 min |
| Est. cost / task | ~$0.90 |
| Plan | Free+ (all tenants) |
Orchestrator note: The Activity Planner generates no content. Its only job is to read the deliverable plan, decide the correct sequence and priority of tasks, and produce a set of Activity records + BullMQ job enqueues. All content is produced by the worker agents it dispatches.
Triggers
| Trigger type | When | Who initiates |
|---|---|---|
| Scheduled / cron | First day of each calendar month at 00:30 UTC — creates the full monthly activity pipeline for every active tenant | Platform scheduler (BullMQ cron) |
| Human on-demand | ”Start campaign” button in Dashboard → Campaigns screen; also fires when admin approves the deliverable plan for the first time | Tenant admin |
| Activity Planner dispatch | When all tasks in a period complete before the period end date, the completion event can trigger a new Planner run to pull forward the next period’s pipeline early | Activity Planner (self-dispatching via completion event) |
Input
interface ActivityPlannerInput {
tenantId: string;
deliverablePlanId: string; // ID of the approved DeliverablePlan record
periodStartDate: string; // ISO 8601 date e.g. "2026-04-01"
periodEndDate: string; // ISO 8601 date e.g. "2026-04-30"
deliverables: DeliverableSpec[]; // From Deliverable Planner output
previousPeriodSummary?: {
periodId: string;
completedDeliverables: string[]; // Types completed last period
skippedDeliverables: string[]; // Types skipped or failed
performanceNotes?: string; // Free-text summary of what worked
topPerformingContent?: string[]; // URLs or titles of best-performing pieces
};
earlyTrigger?: boolean; // true = prior period finished early; run next period
}Output
interface ActivityPlannerOutput {
tenantId: string;
periodId: string; // New Period record ID
generatedAt: string; // ISO 8601 timestamp
activities: ActivitySpec[]; // Activity records to create in DB
jobsEnqueued: EnqueuedJob[]; // BullMQ jobs created
pipelineSummary: string; // Human-readable Markdown summary of the pipeline
totalEstimatedCost: number; // Sum of estimatedCost across all activities
planningNotes: string; // Rationale, sequencing decisions, anything notable
}
interface ActivitySpec {
activityId: string; // UUID — used as BullMQ job ID
agentQueue: string; // e.g. "agent__blog-writer"
deliverableType: string; // e.g. "blog_post"
inputPayload: Record<string, unknown>; // Fully resolved input for the target agent
dependsOn: string[]; // activityId values this must wait for
priority: number; // 1 (highest) to 5 (lowest)
estimatedCost: number; // In platform credits
dueDate: string; // ISO 8601 — when this should be complete by
label: string; // Human-readable e.g. "Blog Post #1 — April 2026"
}
interface EnqueuedJob {
queue: string;
jobId: string;
scheduledFor?: string; // If delayed — ISO 8601
}Sample output excerpt
## April 2026 Campaign Pipeline — Clearflow Plumbing
**Period:** 1 April – 30 April 2026
**Total activities:** 17
**Estimated cost:** 28 credits
### Week 1 (1–7 April)
- [x] Keyword Research — "plumber cost Brisbane" cluster → feeds Blog Posts #1, #2
- [x] Social Calendar — April organic social plan (Facebook + Instagram)
- [ ] Blog Post #1 — "How much does a plumber cost in Brisbane in 2026?" (due 7 Apr)
### Week 2 (8–14 April)
- [ ] Blog Post #2 — "Blocked drain vs burst pipe: what to do first" (due 14 Apr)
- [ ] GBP Post batch #1 — 4 posts scheduled for 8, 10, 12, 14 Apr
- [ ] Google Ads RSA refresh — Emergency plumbing campaign (due 12 Apr)
### Week 3 (15–21 April)
- [ ] Blog Post #3 — "Hot water system lifespan: when to repair vs replace" (due 21 Apr)
- [ ] GBP Post batch #2 — 4 posts scheduled for 15, 17, 19, 21 Apr
- [ ] Social Posts batch #1 — 8 posts scheduled across Facebook and Instagram
### Week 4 (22–30 April)
- [ ] Blog Post #4 — "Plumbing maintenance checklist for Brisbane homeowners" (due 28 Apr)
- [ ] GBP Post batch #3 — finalises month
- [ ] Monthly Report — April performance review (due 30 Apr, depends on all above)
**Dependency chain:** Keyword Research → Content Briefs → Blog Posts → Monthly ReportHow It Works
-
Receive job — BullMQ dequeues the job with
tenantId,deliverablePlanId, period dates, deliverables, and optionalpreviousPeriodSummary. -
RAG: published content (avoid repetition) — Call
rag_searchagainst the Published Content dataset for “content published last month”, “recent blog posts”, “recent GBP posts”. This surfaces recently produced titles and topics so the Planner can instruct downstream agents to avoid repeating them. -
RAG: upcoming events and promotions — Call
rag_searchagainst the Client Documents dataset for “upcoming events”, “promotions”, “seasonal offers”, “product launches”. Any planned events or campaigns are surfaced and used to time-align content — e.g. a promotional offer due mid-month gets a blog post and GBP post scheduled to land the week before. -
Resolve delivery sequence — For each deliverable type in the plan, identify its dependencies (e.g. blog posts depend on content briefs, which depend on keyword research). Build a directed acyclic graph (DAG) of tasks. Assign
dependsOnreferences and calculatedueDatefor each task by working backwards fromperiodEndDate. -
Distribute across the period — Spread tasks across calendar weeks to avoid batching everything at the end of the month. GBP posts are spaced evenly. Blog posts are distributed one per week where volume allows. The monthly report is always the last task.
-
Resolve input payloads — For each
ActivitySpec, populateinputPayloadwith the specific inputs the target agent needs: topic guidance from RAG results, brand settings from tenant config, content brief references where applicable. The Planner writes a specific topic for each blog post — it does not leave topics blank for the writer to decide. -
LLM planning call — Send the full context (deliverables, RAG results, previous period summary, period dates) to Claude Sonnet 4.6 with the Activity Planner system prompt. Model returns the full
ActivityPlannerOutputJSON. -
Persist Activity records — Worker writes one Activity record per task to the
activitiestable. Each record stores theinputPayload,dependsOn,dueDate,status: 'pending', andagentQueue. -
Enqueue BullMQ jobs — For each Activity, enqueue a job to the appropriate agent queue. Jobs with dependencies are enqueued with a
waitChildrenoption or an initialdelayedstatus until their dependencies resolve. High-priority tasks (keyword research, content briefs) are enqueued immediately withpriority: 1. -
Write pipeline summary — Generate the human-readable Markdown pipeline summary and write it to the
periodstable record. This is the document shown to the admin in Dashboard → Campaigns.
System Prompt
You are the Activity Planner — the orchestration brain of a digital marketing agency's AI system. Your job is to read a client's monthly deliverable plan and create the activity pipeline for this period: one task per deliverable unit, sequenced correctly, with specific topics and timing.
TENANT SETTINGS: {{TENANT_SETTINGS}}
TODAY (plan approved / trigger date): {{TRIGGER_DATE}}
PERIOD: {{PERIOD_START_DATE}} to {{PERIOD_END_DATE}}
CLIENT CONTEXT FILE:
{{CLIENT_CONTEXT}}
DELIVERABLE PLAN:
{{DELIVERABLES}}
RECENTLY PUBLISHED CONTENT (avoid repeating these topics):
{{PUBLISHED_CONTENT_RAG}}
UPCOMING EVENTS AND PROMOTIONS:
{{CLIENT_DOCUMENTS_RAG}}
PREVIOUS PERIOD SUMMARY:
{{PREVIOUS_PERIOD_SUMMARY}}
Your task:
1. Create one ActivitySpec for each deliverable unit in the plan
2. Assign a SPECIFIC topic or focus for each content piece — do not leave topics vague or generic
3. Build the correct dependency chain: keyword research → content briefs → blog posts → monthly report
4. Spread tasks across the period so production is consistent week-by-week, not end-loaded
5. Time-align content with any upcoming events or promotions found in the context
6. Distribute activities across the **remaining** window — all dueDates MUST be on or after TODAY (trigger date) and on or before the period end. Never assign a date before TODAY.
7. Avoid repeating topics from the previously published content list
7. Write a human-readable pipeline summary in Markdown (the "what's happening this month" document)
IMPORTANT: You generate NO content yourself. You only plan and sequence. The input payloads you write will be passed directly to the relevant worker agents — be precise and specific.
Output must be valid JSON matching the ActivityPlannerOutput schema.Skills Injected
| Skill file | Purpose |
|---|---|
client-context-file.md | Always injected — provides brand, audience, competitors, and geographic focus to ensure tasks are briefed with the right context |
activity-planning-sop.md | Orchestration playbook — dependency rules, sequencing logic, how to handle plan gaps |
deliverable-types.md | Canonical list of all deliverable types and their agent queues — ensures correct queue routing |
activity-planning-sop.md — content
# Activity Planning SOP — Activity Planner
## Sequencing Rules
### Mandatory dependency chains
1. **Blog posts** always require a `content_brief` which requires a `keyword_cluster`
- Sequence: `keyword-researcher` → `content-brief-writer` → `blog-writer`
2. **Social post batches** always require a `social_calendar`
- Sequence: `social-calendar-planner` → `social-post-writer` (batch)
3. **Monthly report** must always be the last task in the period
- Sequence: all content tasks → `report-writer`
4. **Google Ads copy** requires a `keyword_cluster` to have been run
- Sequence: `keyword-researcher` → `google-ads-writer`
### Tasks with no dependencies (can run immediately)
- GBP posts (standalone)
- Email newsletters (standalone)
- Landing page copy (standalone)
## Timing Guidelines
### Week 1 (days 1–7)
- Enqueue all research tasks immediately (keyword research, topic research)
- Enqueue social calendar
- Enqueue first blog post brief
### Week 2 (days 8–14)
- First blog post should be ready for review
- GBP batch #1 goes live (schedule posts for even spacing)
- Second blog post in production
### Week 3 (days 15–21)
- Second and third blog posts in review/published
- Social post batch #1 scheduled
- Ads copy refresh in production if planned
### Week 4 (days 22–30)
- Final blog posts
- Remaining GBP posts
- Monthly report triggered after all content tasks complete
## Topic Selection Rules
- Never repeat a topic that has been published in the last 90 days
- Blog topics should align with the keyword clusters identified in strategy
- GBP posts rotate through: service spotlight, seasonal tip, promotion, testimonial reference
- For the first period, default to the top content gap identified in the competitor analysis
## Priority Scores
1. Keyword research (everything depends on it)
2. Content briefs (blog posts depend on them)
3. Blog posts (highest SEO value)
4. GBP posts (consistent local presence)
5. Social posts (brand building)
6. Ads copy (paid channel support)
7. Monthly report (end-of-period; depends on all above)
## Handling Plan Gaps
If a deliverable type in the plan has no viable topic (all obvious topics recently covered), create the activity with a `topicNote: "Select a fresh angle — all primary topics recently covered"` in the input payload and flag it in `planningNotes`.RAG Usage
| Dataset | Query example | When used |
|---|---|---|
| Published Content | "blog posts published last month", "recent GBP posts", "published content last 90 days" | Step 2 — avoids topic repetition; informs what has already been covered |
| Client Documents | "upcoming events", "promotions", "product launches", "seasonal campaigns" | Step 3 — time-aligns content with the client’s business calendar |
| Website Content | Not queried | Not applicable at pipeline creation stage |
| Competitor Research | Not queried | Competitor context already captured in Client Context File |
Tools Required
| Tool | Method | Purpose | Required? |
|---|---|---|---|
rag_search | search | Query Published Content to avoid topic repetition | Yes |
rag_search | search | Query Client Documents for upcoming events and promotions | Yes |
The Activity Planner has no external web tools. It reads from the database context (via RAG and job payload) and writes Activity records and BullMQ jobs. All external calls are made by the worker agents it dispatches.
HITL Gates
Gate 0 — DM pipeline review (optional, per-tenant or global)
When requireActivityApproval is enabled, the activity planner pauses after writing Activity records and sets the DeliverablePeriod to dm_review instead of immediately dispatching jobs. A DM reviewer must approve before any agent runs.
The value is resolved in order: TenantDeliverableConfig.requireActivityApproval (per-tenant override, set in Manage → Tenants → [Tenant] → Deliverable Config) → PlatformSetting("requireActivityApproval") (global, set in Manage → System → Deliverable Settings) → true (default ON).
DM reviewer actions (DM portal → Pipeline screen):
- Review the full list of planned activities (label, type, priority, due date)
- Edit any activity’s label, priority, or due date
- Delete any activity from the pipeline
- Add a new ad-hoc activity (type dropdown → auto-routes to correct agent queue)
- Approve — enqueues dependency-free activities; period moves to
active - Reject — enter a rejection reason; all current activities are deleted; period moves to
rejected; activity planner is re-enqueued withrejectionFeedbackinjected into the Claude prompt
Rejection reasons are stored in DeliverablePeriodLog with event: "dm_rejected" and rejectionReason field, so the timeline is auditable. The feedback is passed to the re-enqueued job as rejectionFeedback in ActivityPlannerJobData.
Auto-approve after timeout: Planned for a future iteration — not currently implemented. See
TODOin Deliverable Settings screen.
Gate 1 — Pipeline preview (first run and on-demand, when DM approval is off) After the pipeline is created, it is surfaced in Dashboard → Campaigns as a timeline view before any worker jobs are processed. The admin sees:
- A week-by-week breakdown of all planned tasks
- The specific topic assigned to each blog post and content piece
- Total credit estimate for the period
Admin actions:
- Edit the topic for any specific task before it runs
- Remove a task from this period (“skip this month”)
- Add an ad-hoc task (e.g. “I need a landing page for a new service”)
- Approve the pipeline (“Start producing”)
Jobs are not dispatched to worker queues until the admin approves the pipeline (first run). On scheduled monthly runs, the pipeline auto-approves after 48 hours if the admin has not reviewed it.
Gate 2 — Mid-period block If a critical-path task fails after all retries and blocks downstream tasks (e.g. keyword research fails → all blog posts blocked), an in-app notification is sent to the admin and the blocked tasks are surfaced in the Campaigns screen with the failure reason. The admin can:
- Retry the failed task
- Manually provide the input the failed task was supposed to produce
- Cancel the blocked tasks for this period
Guardrails
activitiesarray must contain at least 1 entry.- Every blog post activity must have a
content_briefactivity in itsdependsOnlist. - The monthly report activity (if present) must have
dependsOnreferencing all other activities in the period. - No two activities may have the same topic or title (checked against both the current pipeline and recently published content).
inputPayloadfor every activity must be a non-empty object — the Planner must provide specific inputs, not empty shells.agentQueuevalues must exactly match canonical queue names.- Total
estimatedCostmust not exceed the tenant’s monthly credit limit + 10% tolerance.
Tenant Settings Used
| Setting | How it’s used |
|---|---|
tenantId | Keys all Activity and Period records created |
plan | Constrains which deliverable types can be in the pipeline |
connectedChannels | Excludes tasks for channels without active OAuth connections |
companyName | Used in task labels and pipeline summary heading |
industry | Informs topic selection logic and seasonal relevance |
brandColors | Passed in social post and ads activity payloads for downstream agents |
Cost Profile
| Avg input tokens | ~14,000 (deliverable plan + RAG results + previous period summary + system prompt) |
| Avg output tokens | ~2,200 (large output — full pipeline with resolved payloads for every task) |
| Est. cost / task | ~$0.90 |
Error Handling
| Error | Response |
|---|---|
| No approved deliverable plan found | Fail immediately; surface error in Dashboard: “No deliverable plan has been approved for this tenant. Go to Strategy → Deliverable Plan to approve one.” |
deliverables array is empty | Fail; same message as above |
| All topics already published (nothing new to plan) | Flag in planningNotes; create activities with topicNote: "All primary topics recently covered — agent must select a fresh angle" rather than blocking |
| RAG returns no published content | Proceed without repetition-check; note in planningNotes |
| RAG returns no client documents (no upcoming events) | Proceed without event-alignment; standard week-by-week distribution |
| LLM returns malformed JSON | Internal retry (up to 2 Claude calls): attempt 2 sends a JSON-only correction prompt with the previous output preview. After both internal attempts fail, the BullMQ job fails and is retried up to 4 times total (exponential backoff). After all attempts exhausted, failure email + in-app notification sent to client and DM reviewers. |
| Job enqueue fails for a specific queue | Retry 3 times; if still failing, mark that Activity as status: 'enqueue_failed' and surface in HITL; remaining activities are enqueued normally |
| Period already has an active pipeline | Abort; log: “A pipeline already exists for period [periodId]. Use the Campaigns screen to manage it.” |
| Credit limit would be exceeded | Re-prompt model to reduce volumes; if still over after retry, surface hard warning in pipeline preview and require explicit admin override to proceed |
Re-triggering After Failure
If the activity planner fails after all retries, the plan stays approved in the DB but no DeliverablePeriod is created. A “Regenerate Activities” button in the Manage portal (tenant detail → Activities tab) lets superadmins manually re-enqueue the activity planner:
- Endpoint:
POST /admin/v1/tenants/:tenantId/retrigger-activity-planner - Clears the stale BullMQ dedup key and removes any failed job before re-enqueueing
- Requires an
approveddeliverable plan to exist for the tenant