Workflow Model
The Complete Journey
Tenant registers (provides company website URL)
│
├─► Website crawler starts immediately (agent__tenant-web-crawler)
│ Playwright BFS crawl — pages, images, documents stored in DB
│ Homepage analysed for brand signals → colors + fonts written to brand_assets
│
├─► Connect Channels (LinkedIn, Facebook, WordPress, GBP, etc. via OAuth)
│
▼
Onboarding Wizard (/onboarding — 6 steps)
Step 1: Connect Channels
Step 2: Brand Voice (tone, personality, writing style)
Step 3: Brand Assets (colors + fonts — pre-filled from website crawl if found)
Step 4: Documents (upload brand docs)
Step 5: Baseline (current traffic, ads, social metrics)
Step 6: Launch → kicks off setup chain
│
▼
Onboarding Agent (setup chain)
Researches client: website, competitors, industry, products
Generates → Client Context File (skill injected into all agents)
│
▼
Strategy Agent
Reads context file
Generates → Marketing Strategy (goals + recommended deliverables + rationale)
Human reviews and approves strategy
│
▼
Goals
High-level outcomes the strategy aims to achieve
e.g. "Increase organic website traffic", "Improve local search presence"
Each goal has a metric and target
│
▼
Deliverables (per goal, recurring monthly)
Fixed output commitments: 10 blogs, 12 social posts, 10 backlinks, etc.
Each deliverable has a monthly period tracker
│
▼
Requests (BlogRequest / SocialMediaRequest)
Raised by an agent (from the deliverable pipeline) OR by a DM team member ad-hoc
Defines WHAT to create: topic, brief, target audience, target channel(s)
Goes through approval before any content is created
│ approved
▼
Content Entities (BlogPost / SocialPost)
Created automatically when the request is approved
The actual content that agents will write
│
▼
Activities
Agent writes content → Human reviews → Approved → Published to Channel
Completing all activities → deliverable unit fulfilled
Fulfilling all deliverables → goals metConcepts Defined
Client Context File
A Markdown skill document auto-generated by the Onboarding Agent. Contains:
- Company overview, products, USPs
- Target audience
- Brand tone and voice
- Competitors
- Point of contact
This file is injected into every agent that executes activities for this tenant. It is the single source of truth for who the client is.
Created by: Onboarding Agent (initial generation) + human review/edit. Stored as: A skill file in MongoDB, assigned to all agent roles for this tenant. Updated by: Tenant can edit at any time in Settings → Skills.
Strategy
The marketing roadmap generated from the context file. Defines:
- What goals to pursue
- Which deliverable types will meet those goals
- Monthly volumes for each deliverable
- Platforms to focus on
- Recommended timeline
Created by: Strategy Agent.
Reviewed and approved by: Human (DM Portal approval step).
Stored in: PostgreSQL strategies table.
A tenant has one active strategy at a time. Strategies are versioned — a new one can be created without deleting the old one.
Goals
High-level outcomes. Tied to a strategy. Measurable.
Examples:
- Increase organic website traffic by 40% in 6 months
- Rank in top 3 for 5 target keywords
- Generate 50 qualified leads/month via content
- Improve local search visibility (Google Business Profile)
- Build domain authority from DA 20 to DA 40
Each goal has:
- Name and description
- Primary metric (sessions, rankings, leads, DA score)
- Target value and target date
- Status (on-track / at-risk / achieved)
Deliverables
Fixed, recurring monthly commitments that collectively fulfill the goals. The agency commits to delivering these every month.
Examples:
| Deliverable Type | Volume | Linked Goal |
|---|---|---|
| Blog posts | 10/month | Increase organic traffic |
| Social posts | 12/month | Brand awareness + engagement |
| Backlinks | 10/month | Domain authority improvement |
| Google Business Profile posts | 10/month | Local search visibility |
| Website audit | 1/month | Technical SEO health |
| Email newsletter | 2/month | Lead nurture |
| Ad copy sets | 4/month | Paid acquisition |
| Performance report | 1/month | Client visibility |
Each deliverable has:
- Type and monthly target quantity
- Linked goal(s)
- Monthly period tracker (how many completed this month vs. target)
- Status:
on_track|at_risk|completed|missed
Channels
Channels are the external platforms a tenant connects via OAuth from their Dashboard. Each channel is a publishing destination — agents use the stored credentials to post content after human approval.
Examples:
- LinkedIn Company Page
- Facebook Business Page
- Instagram Business Account
- WordPress site (via REST API + application password)
- Google Business Profile (GBP)
- Google Search Console (read-only — for performance data)
- X (Twitter)
- TikTok Business Account
- Mailchimp / Klaviyo (email)
Connection flow:
- Tenant opens Dashboard → Channels (
/channels) - Clicks “Connect” for a platform → OAuth flow begins (redirect to platform)
- Platform redirects back with auth code → system exchanges for access + refresh tokens
- Channel record created with encrypted tokens + metadata (page ID, account name, etc.)
- Agent configs updated — agents can now post to this channel
Token management:
- Access tokens stored encrypted at rest
- Refresh tokens stored encrypted at rest
- System automatically refreshes access tokens before expiry
- If refresh fails → channel status set to
expired→ alert created for tenant admin
Channel types:
social— LinkedIn, Facebook, Instagram, X, TikTok (publish posts)cms— WordPress, Webflow (publish blog posts)analytics— Google Search Console, GA4 (read performance data)local— Google Business Profile (publish GBP posts)email— Mailchimp, Klaviyo (send email campaigns)ads— Google Ads, Meta Ads (manage ad campaigns)Website— tenant’s own website; Playwright BFS crawler stores pages + media + brand signals; auto-created at signup when website URL is provided
Requests (BlogRequest / SocialMediaRequest)
A Request is the brief or specification for a piece of content — raised before any content is created. Requests decouple the INTENT (what we want to make) from the EXECUTION (actually making it).
Who can raise a request:
- 🤖 An agent (e.g. SEO Specialist raising blog requests as part of the deliverable pipeline)
- 👤 A DM team member (ad-hoc request from the DM Portal)
- 👤 A tenant admin (ad-hoc request from the Dashboard)
Request lifecycle:
draft → pending_approval → approved → in_progress → completed
→ rejected → (closed or revised)On approval → content entity auto-created:
BlogRequestapproved →BlogPostrecord created (status:draft)SocialMediaRequestapproved →SocialPostrecord(s) created (one per target channel)
Key properties of a request:
- Title / topic brief
- Target keyword(s) (blog only)
- Target audience
- Target channel(s) — which connected channel(s) the content will publish to
- Notes / creative direction
- Priority
- Linked deliverable (if part of automated pipeline) or ad-hoc (if manual)
Content Entities (BlogPost / SocialPost)
Content entities are created automatically when a Request is approved. They represent the actual content that agents will write.
BlogPost:
- Linked to the
BlogRequestthat created it - Has a lifecycle:
draft → writing → review → approved → scheduled → published - Content stored in MongoDB (can be large — full article with formatting)
- Published to a
cmschannel (WordPress, Webflow) using stored OAuth tokens
SocialPost:
- One record per platform/channel (a request targeting LinkedIn + Facebook = 2 SocialPost records)
- Each platform may have different content (different lengths, formats, hashtags)
- Lifecycle:
draft → writing → review → approved → scheduled → published - Published to the target
socialchannel using stored OAuth tokens
Activities
The atomic unit of work. Everything that happens in the system is an activity.
An activity can be:
- An agent task (research topics, write a blog post, build backlink list)
- A human task (review and approve topics, final copy review)
- An approval gate (HITL sign-off before content proceeds)
Key properties:
- Assigned to a principal: either an AI agent or a human user
- Has a clear input and expected output
- Progresses through a status lifecycle
- May have a parent activity (sub-tasks)
- May be blocked by another activity (dependencies)
Activities are the core unit — the thing you create, assign, track, complete, and report on.
Activity Lifecycle
created → assigned → in_progress → awaiting_approval → approved → completed
│ │
▼ ▼
failed rejected → back to in_progress (retry)Human activities follow the same lifecycle but the “execution” is the human completing the task in the DM Portal, not an agent call.
Example: Producing 10 Blog Posts per Month
This shows how a single deliverable (10 blog posts/month) maps to a pipeline with Requests, content entities, and channel publishing.
Deliverable: 10 Blog Posts — April 2026
Channel: WordPress (acmecorp.com) — connected via OAuth
┌─────────────────────────────────────────────────────────────┐
│ Activity: Research 10 Blog Topics │
│ Assigned to: 🤖 SEO Specialist + Content Researcher │
│ Output: 10 BlogRequests created (status: pending_approval) │
│ Each has: topic, target keyword, search intent │
└───────────────────────────┬─────────────────────────────────┘
│ completes
▼
┌─────────────────────────────────────────────────────────────┐
│ Activity: Review & Approve Blog Requests │
│ Assigned to: 👤 Human (DM Portal) │
│ Input: 10 BlogRequests │
│ Action: approve all / reject some / edit brief │
│ On approval of each: BlogPost entity auto-created (draft) │
└───────────────────────────┬─────────────────────────────────┘
│ approved (e.g. 10/10)
▼ (one sub-pipeline per approved BlogPost)
┌─────────────────────────────────────────────────────────────┐
│ Activity: Write Blog Post — "{Topic Title}" [×10] │
│ Assigned to: 🤖 Copywriter │
│ Input: BlogRequest brief + SEO brief + client context │
│ Output: BlogPost content updated (status: review) │
└───────────────────────────┬─────────────────────────────────┘
│ completes
▼
┌─────────────────────────────────────────────────────────────┐
│ Activity: Review & Approve Blog Post — "{Topic Title}" [×10]│
│ Assigned to: 👤 Human (DM Portal) │
│ Action: approve / reject with notes / edit inline │
│ On approval: BlogPost status → approved │
└───────────────────────────┬─────────────────────────────────┘
│ approved
▼
┌─────────────────────────────────────────────────────────────┐
│ Activity: Publish to WordPress — "{Topic Title}" [×10] │
│ Assigned to: 🤖 Copywriter (using WordPress channel tokens) │
│ Action: POST to WordPress REST API using stored OAuth token │
│ Output: BlogPost.published_url set; status → published │
└───────────────────────────┬─────────────────────────────────┘
│ published (10/10)
▼
┌─────────────────────────────────────────────────────────────┐
│ Deliverable COMPLETED: 10 Blog Posts ✅ │
│ Goal progress updated: Organic Traffic goal +10 blogs │
└─────────────────────────────────────────────────────────────┘Ad-hoc blog request (outside of deliverable pipeline):
A DM team member or tenant admin can raise a BlogRequest at any time from the Dashboard or DM Portal without it being part of the monthly deliverable cycle. These go through the same approval → write → review → publish pipeline but are not counted against any deliverable quota.
Example: 10 Backlinks per Month
Deliverable: 10 Backlinks — April 2026
Goal: Improve Domain Authority
Activity: Research 10 Backlink Opportunities
→ 🤖 SEO Specialist (Ahrefs/SEMrush tools)
→ Output: list of 10 target sites with contact details + angle
Activity: Review Backlink Opportunities
→ 👤 Human approval (filter out irrelevant sites)
→ Output: approved target list
Activity: Write Outreach Email for each target [×10]
→ 🤖 Copywriter
→ Output: personalised outreach email per site
Activity: Review Outreach Emails [×10]
→ 👤 Human (DM Portal)
→ Action: approve / edit / reject
Activity: Send Outreach [×10]
→ 👤 Human (manually send) OR 🤖 via email integration
→ Output: sent confirmation + tracking
Deliverable: 10 Backlinks Outreached ✅Example: 12 Social Posts per Month
Deliverable: 12 Social Posts — April 2026 (3×/week)
Channels: LinkedIn Company Page + Instagram Business (both connected)
Goal: Brand Awareness + Engagement
┌──────────────────────────────────────────────────────────────┐
│ Activity: Create Social Content Calendar │
│ Assigned to: 🤖 Social Media Manager │
│ Output: 12 SocialMediaRequests (status: pending_approval) │
│ Each has: topic, platform(s), format, brief, date │
└────────────────────────────┬─────────────────────────────────┘
│ completes
▼
┌──────────────────────────────────────────────────────────────┐
│ Activity: Review & Approve Content Calendar │
│ Assigned to: 👤 Human (DM Portal) │
│ Input: 12 SocialMediaRequests │
│ Action: approve / edit / reject individual requests │
│ On approval of each: SocialPost records created │
│ → 1 SocialPost per channel (LinkedIn + Instagram = 2 each) │
└────────────────────────────┬─────────────────────────────────┘
│ approved
▼ (one sub-pipeline per approved SocialPost)
┌──────────────────────────────────────────────────────────────┐
│ Activity: Write Post Content — [×12 requests, ×2 channels] │
│ Assigned to: 🤖 Copywriter / Social Media Manager │
│ Input: SocialMediaRequest brief + channel format rules │
│ Output: Platform-specific copy per SocialPost │
│ LinkedIn: longer, professional tone (up to 3000 chars) │
│ Instagram: punchy, hashtags, emoji (up to 2200 chars) │
└────────────────────────────┬─────────────────────────────────┘
│ completes
▼
┌──────────────────────────────────────────────────────────────┐
│ Activity: Review & Approve Posts [×24 SocialPosts] │
│ Assigned to: 👤 Human (DM Portal) │
│ Action: approve / edit / reject per post per platform │
│ On approval: SocialPost status → approved │
└────────────────────────────┬─────────────────────────────────┘
│ approved
▼
┌──────────────────────────────────────────────────────────────┐
│ Activity: Publish to Channel — [×24 SocialPosts] │
│ Assigned to: 🤖 Social Media Manager (using channel tokens) │
│ LinkedIn: POST /v2/ugcPosts using stored LinkedIn token │
│ Instagram: POST via Graph API using stored Instagram token │
│ Output: SocialPost.published_url; status → published │
└────────────────────────────┬─────────────────────────────────┘
│ all 12 requests published
▼
Deliverable COMPLETED: 12 Social Posts ✅
Goal progress updated: Brand Awareness goalExample: 10 GBP Posts per Month
Deliverable: 10 Google Business Profile Posts — April 2026
Channel: Google Business Profile (acmecorp) — connected via GBP OAuth
Goal: Improve Local Search Visibility
Activity: Create 10 GBP Post Requests
→ 🤖 Social Media Manager
→ Output: 10 SocialMediaRequests (type: gbp_post, status: pending_approval)
Activity: Review GBP Post Requests
→ 👤 Human (DM Portal)
→ On approval: SocialPost records created (channel: GBP)
Activity: Write each GBP Post [×10]
→ 🤖 Copywriter (short format, local intent, 1500 char limit)
→ Output: SocialPost content updated (status: review)
Activity: Review GBP Posts [×10]
→ 👤 Human final approval
→ On approval: SocialPost status → approved
Activity: Publish to GBP [×10]
→ 🤖 Social Media Manager
→ POST via Google Business Profile API using stored OAuth token
→ SocialPost.published_url set; status → published
Deliverable: 10 GBP Posts Published ✅Channels Scoring
When a channel is first connected — and monthly thereafter — the Data Analyst calculates a Channel Health Score (0–100) for each connected channel. The score reflects how well the channel is set up and performing.
Score components vary by platform:
| Platform | Score dimensions |
|---|---|
| LinkedIn Page | Profile completeness, post frequency, engagement rate, follower growth, content variety |
| Profile completeness, post frequency, engagement rate, story usage, hashtag quality | |
| Facebook Page | Completeness, response rate, post frequency, review rating |
| Google Business Profile | Profile completeness (hours, photos, description), review count + avg rating, post frequency |
| WordPress | Technical SEO health, content freshness, avg word count, internal linking |
Score lifecycle:
- Channel connected → initial score calculated immediately
- Low-scoring areas → suggested improvement activities auto-created (e.g. “Upload LinkedIn cover image”, “Respond to 3 unanswered Google reviews”)
- Monthly re-score → score delta shown in Dashboard
- Improving the score is itself a deliverable goal (“Improve LinkedIn score from 68 to 80 in 3 months”)
Score history stored in channel_scores table per month for trend tracking.
Mini CRM — Lead Tracking
The system includes a lightweight CRM to track leads collected from all channels and campaigns. Growing the number of qualified leads is a first-class goal type that the strategy can target.
Lead sources
Leads flow in from:
- Website forms — via webhook (tenant embeds a script or webhook URL in their contact form)
- Facebook Lead Ads — via Meta Webhooks (auto-captured when a lead form is submitted)
- Google Ads Lead Form Extensions — via Google Ads API or webhook
- Manual entry — DM team or tenant manually adds a lead
- Imported — CSV upload
Lead lifecycle
Lead captured (from any source)
│
▼ (auto-enrichment activity — optional)
🤖 Enrichment: look up company info, LinkedIn profile, etc.
│
▼
Lead visible in CRM (status: new)
│
▼ Human or agent qualifies the lead
│
├── Qualified → lead.status = qualified
│ │
│ ▼
│ (optional) Agent drafts follow-up email
│ Human reviews + approves → sent via email channel
│
└── Disqualified → lead.status = disqualified, reason notedRelationship to goals
“Generate 50 qualified leads/month” is a strategy goal. The Data Analyst updates goals.current_value monthly by counting leads WHERE status = 'qualified' AND created_on >= period_start.
Paid Campaigns Workflow
Paid campaigns (Google Ads, Meta Ads) follow a different pattern from content creation. Instead of creating new content, the agent syncs existing campaign data, classifies it, proposes optimizations, and pushes approved changes back.
Google Ads Flow
Channel connected (Google Ads OAuth)
│
▼ (on connect + periodic sync)
┌─────────────────────────────────────────────────────────────┐
│ Activity: Sync Google Ads Campaigns │
│ Assigned to: 🤖 Paid Ads Manager │
│ Action: Pull campaigns, ad groups, keywords, search terms │
│ Output: `ad_campaigns`, `ad_groups`, `ad_keywords`, │
│ `search_term_reports` records upserted in DB │
└───────────────────────────┬─────────────────────────────────┘
│ completes
▼
┌─────────────────────────────────────────────────────────────┐
│ Activity: Classify Search Terms │
│ Assigned to: 🤖 Paid Ads Manager │
│ Input: Raw search terms from sync │
│ Output: Each term classified as: │
│ - keyword (add as exact/phrase match) │
│ - negative_keyword (exclude) │
│ - watch (monitor, no action yet) │
│ Records: `search_term_classifications` created │
└───────────────────────────┬─────────────────────────────────┘
│ completes
▼
┌─────────────────────────────────────────────────────────────┐
│ Activity: Review Search Term Classifications │
│ Assigned to: 👤 Human (DM Portal / Dashboard) │
│ Input: classified terms with recommended action │
│ Action: approve / override classification / skip │
│ On approval: push to Google Ads API │
│ - keyword → add to ad group as exact/phrase match │
│ - negative → add to campaign negative keyword list │
└───────────────────────────┴─────────────────────────────────┘
Separately (monthly performance cycle):
┌─────────────────────────────────────────────────────────────┐
│ Activity: Generate Optimization Recommendations │
│ Assigned to: 🤖 Paid Ads Manager │
│ Input: Campaign performance data (CTR, CPC, Conv rate, ROAS)│
│ Output: `ad_optimization_recommendations` records created │
│ e.g. "Increase bid for X by 15% — reason: CTR 8.2%" │
└───────────────────────────┬─────────────────────────────────┘
│ completes
▼
┌─────────────────────────────────────────────────────────────┐
│ Activity: Review & Approve Optimizations │
│ Assigned to: 👤 Human (DM Portal / Dashboard) │
│ Action: approve / reject / snooze per recommendation │
│ On approval: push changes to Google Ads API │
│ (bid adjustments, budget changes, pause/enable keywords) │
└─────────────────────────────────────────────────────────────┘Meta Ads Flow
Same pattern as Google Ads:
- Sync — pull campaigns, ad sets, ads, audience data from Meta Ads API
- Analyse — Paid Ads Manager analyses performance (CPC, CPM, CTR, ROAS, frequency)
- Recommend — create
ad_optimization_recommendations(pause underperforming ads, adjust budgets, refresh creatives, update audiences) - Review — human approves/rejects each recommendation
- Push — approved changes applied via Meta Marketing API
Key Distinction from Content Workflows
| Content (Blog/Social) | Paid Campaigns |
|---|---|
| Agent CREATES new content | Agent ANALYSES existing data |
| Request → Entity → Write → Review → Publish | Sync → Classify/Recommend → Review → Push |
BlogRequest → BlogPost | SearchTermReport → Classification → Recommendation |
| Approval gates on creative decisions | Approval gates on account changes (money involved) |
Two Types of Activity Templates
The system has two distinct template concepts:
- Pipeline Templates — multi-step blueprints for producing a deliverable (e.g., 10 blog posts = research → approve → write × 10 → review × 10)
- Recurring Task Templates — standalone predefined tasks that recur on a schedule, not tied to a deliverable (e.g., “Update Google My Business listing”, “Send monthly report to client”)
Recurring Task Templates (Predefined Tasks)
Some activities are not part of a deliverable pipeline but are still recurring operational tasks that every tenant needs. These are defined as Recurring Task Templates.
How they work
Global Recurring Task Templates (managed in Manage App)
│
│ On tenant creation: copy global templates → tenant-specific templates
▼
Tenant Recurring Task Templates (tenant can customise)
│
│ Based on periodicity + trigger: auto-create activities
▼
Activities (created on schedule or on trigger)Template properties
interface RecurringTaskTemplate {
id: string;
tenantId: string | null; // null = global template
name: string; // e.g. "Update Google My Business listing"
description: string;
assigneeType: 'agent' | 'human';
agentRole?: AgentRole;
humanRole?: 'reviewer' | 'admin';
periodicity: 'one_time' | 'daily' | 'weekly' | 'monthly' | 'quarterly';
trigger: 'on_onboarding' | 'on_strategy_approval' | 'cron' | 'manual';
cronExpression?: string; // if trigger = 'cron'
promptTemplate: string; // task description with {{variables}} for substitution
variables: TemplateVariable[]; // filled in per-tenant (e.g. GBP URL)
isActive: boolean;
sourceTemplateId: string | null; // global template this was copied from
}
interface TemplateVariable {
key: string; // e.g. "gbp_url"
label: string; // e.g. "Google Business Profile URL"
type: 'string' | 'url' | 'number' | 'select';
required: boolean;
value?: string; // filled in at tenant level
}Examples
| Template Name | Trigger | Periodicity | Agent/Human | Variables |
|---|---|---|---|---|
| Update “always open” in Google My Business | on_onboarding | one_time | 👤 Human | gbp_url |
| Request Google reviews from recent customers | monthly | monthly | 👤 Human | gbp_url, customer_contact_method |
| Verify NAP consistency across directories | quarterly | quarterly | 🤖 SEO Specialist | business_name, address |
| Add new product/service to GBP | manual | — | 👤 Human | gbp_url |
| Archive completed campaign assets | monthly | monthly | 👤 Human | — |
| Update copyright year on website | yearly | yearly | 👤 Human | website_url |
Tenant variable substitution
When a recurring task template is copied to a tenant, tenant-specific variables are filled in:
// Global template prompt:
// "Go to {{gbp_url}} and set the listing to 'Always Open'"
// After tenant fills in variables:
// "Go to https://business.google.com/u/0/dashboard/l/ABC123 and set the listing to 'Always Open'"Variables that are not yet filled in are flagged on the tenant’s Setup Checklist.
Pipeline Activity Templates
Rather than creating activity pipelines from scratch for each deliverable, the system uses activity templates — pre-defined pipelines per deliverable type.
When a deliverable period starts (e.g. April blog posts due), the system automatically spawns the activity pipeline from the template.
interface ActivityTemplate {
deliverableType: DeliverableType;
steps: ActivityTemplateStep[];
}
interface ActivityTemplateStep {
name: string;
assigneeType: 'agent' | 'human';
agentRole?: AgentRole; // if agent
humanRole?: 'reviewer' | 'approver' | 'admin'; // if human
inputFrom?: string; // step name whose output feeds this step
isApproval: boolean;
repeatPerUnit?: boolean; // true = one activity per blog post, not one for all
blockedBy?: string[]; // step names that must complete first
}Template: Blog Post Pipeline
const blogPostTemplate: ActivityTemplate = {
deliverableType: 'blog_posts',
steps: [
{
name: 'Research topics',
assigneeType: 'agent',
agentRole: 'seo-specialist',
isApproval: false,
},
{
name: 'Approve topic list',
assigneeType: 'human',
humanRole: 'reviewer',
inputFrom: 'Research topics',
isApproval: true,
blockedBy: ['Research topics'],
},
{
name: 'Write blog post',
assigneeType: 'agent',
agentRole: 'copywriter',
inputFrom: 'Approve topic list',
isApproval: false,
repeatPerUnit: true, // one per approved topic
blockedBy: ['Approve topic list'],
},
{
name: 'Review and approve blog post',
assigneeType: 'human',
humanRole: 'reviewer',
inputFrom: 'Write blog post',
isApproval: true,
repeatPerUnit: true,
blockedBy: ['Write blog post'],
},
],
};Monthly Cadence
At the start of each month, the system automatically:
- Creates a
DeliverablePeriodrecord for each active deliverable (e.g. “April 2026 Blog Posts”) - Spawns the first activity in each deliverable’s pipeline
- Notifies the assigned human or agent
Agents work on their assigned activities. Humans review and approve in the DM Portal. As activities complete, downstream activities are unblocked and spawned automatically.
Progress tracking:
deliverable_periods.completed_countincrements as each unit is finished- Goal
progress_valueis updated as deliverables complete - Dashboard shows: “Blog Posts: 6/10 completed this month”
How Activities Relate to the Control Plane
When an activity is assigned to an agent:
- Control plane creates an
activitiesrecord - Enqueues the activity to the correct BullMQ queue (tenant:agentType)
- Agent adapter dispatches the task to the agent runtime
- Agent executes, phones home with output
- Output stored in MongoDB (activity output)
- Activity status →
awaiting_approvalif the next step is a human review, else →completed - Next activity in the pipeline is spawned
When an activity is assigned to a human:
- Control plane creates an
activitiesrecord withassignee_type: 'human' - DM Portal shows it in the assignee’s queue
- Human completes the activity (approves, edits, rejects)
- Activity status →
completedorrejected - Next activity spawned (or previous re-triggered on rejection)