Goal
Execute a scheduled job run: generate N posts and schedule them directly (autopilot mode) — BUT only if governance allows. Falls back to Inbox if not permitted.
V2 Changes
- Direct scheduling capability — for trusted agents when Safe Mode OFF
- Workspace-level Safe Mode enforcement — if ON, downgrades to inbox routing
- Role-gated — only Editor/Admin agents can use this skill
- Graceful degradation — if scheduling denied, routes to inbox instead of failing
- Same agent memory integration as
bolta.cron.generate_to_review
When to Use This Skill vs bolta.cron.generate_to_review
| Skill | Use When | Routing |
|---|---|---|
bolta.cron.generate_to_review |
Safe automation (always human review) | Always → Inbox |
bolta.cron.generate_and_schedule |
Autopilot (trusted agents, proven performance) | → Scheduled (if allowed) OR → Inbox (if not) |
Governance Decision Tree
Can this agent schedule directly?
├─ Is Safe Mode ON?
│ └─ YES → route to Inbox (Safe Mode overrides everything)
│ └─ NO → check role
│ ├─ Is agent role Editor or Admin?
│ │ └─ YES → schedule directly ✅
│ │ └─ NO → route to Inbox (Creator/Viewer always need approval)
Policy Rules
- Safe Mode ON → always route to Inbox (regardless of agent role)
- Safe Mode OFF + Editor/Admin role → schedule directly
- Safe Mode OFF + Creator/Viewer role → route to Inbox
- Scheduling requires valid schedule_times — if missing or malformed, route to Inbox
- Voice soft-delete → pause job immediately
- Account disconnected → pause job immediately
Steps
Pre-flight checks (same as generate_to_review):
bolta.get_workspace_policy(workspace_id)→ extractsafe_modebolta.get_my_capabilities(workspace_id)→ verify scheduling capabilitybolta.get_voice_profile(voice_profile_id)→ verify activebolta.get_account_info(account_id)→ verify connected- If voice deleted or account disconnected: fail and pause job
Determine execution mode:
if safe_mode == true: execution_mode = "inbox" reason = "Safe Mode ON" else if agent.role in ["editor", "admin"]: execution_mode = "schedule" else: execution_mode = "inbox" reason = "Agent role requires approval"Load agent memory for context (same as generate_to_review):
bolta.recall(agent_id, "top_performing_topics")bolta.recall(agent_id, "preferred_hook_style")bolta.recall(agent_id, "best_posting_times")→ use if schedule_times not provided
Load recent posts:
bolta.list_recent_posts(account_ids, limit=10)→ avoid repetition
Generate N posts (same logic as generate_to_review):
- For each of
n_posts:- Render template OR generate from voice + memory
- Apply
run_instructionsif provided bolta.draft_post(...)→ collectpost_id
- For each of
Route based on execution mode:
If execution_mode == "schedule":
For each post (index i):
Get schedule time:
- If
schedule_times[i]provided → use it - Else if agent memory has
best_posting_times→ use learned time - Else → add warning, fall back to inbox for this post
- If
Validate schedule time:
- Must be future timestamp (> now + 5 minutes)
- If invalid → add warning, route this post to inbox
bolta.schedule_post(post_id, schedule_time)- On success → add to
scheduled_post_ids - On failure (API error, platform issue) → add to
inbox_post_ids+ warning
- On success → add to
If ALL posts scheduled successfully:
final_state = "scheduled"
If SOME scheduled, SOME to inbox:
final_state = "mixed"- Create inbox_item for the failed ones
If NONE scheduled (all failed):
final_state = "inbox"- Create single inbox_item for all posts
If execution_mode == "inbox":
- All posts transition to
inboxstate - Create
inbox_item_idfor the bundle final_state = "inbox"- Add
reasonto warnings (why downgraded: Safe Mode ON, role insufficient, etc.)
Update agent memory:
bolta.remember(agent_id, "last_scheduled_run", ISO_timestamp)- If execution_mode == "schedule":
bolta.remember(agent_id, "scheduled_times_used", schedule_times)→ learn optimal times
- If any posts failed to schedule:
bolta.remember(agent_id, "recent_failures", failure_reasons)→ avoid repeating
Return execution summary:
run_idscheduled_post_ids(posts that were scheduled)inbox_post_ids(posts that were routed to inbox)inbox_item_id(if any posts went to inbox)final_state(scheduled | inbox | mixed)warnings(downgrade reasons, scheduling failures, etc.)tokens_used
Output Examples
Success (all scheduled)
{
"run_id": "uuid",
"scheduled_post_ids": ["uuid1", "uuid2", "uuid3"],
"inbox_post_ids": [],
"inbox_item_id": null,
"final_state": "scheduled",
"warnings": [],
"tokens_used": { "prompt": 1234, "completion": 567 }
}
Downgraded (Safe Mode ON)
{
"run_id": "uuid",
"scheduled_post_ids": [],
"inbox_post_ids": ["uuid1", "uuid2", "uuid3"],
"inbox_item_id": "bundle-uuid",
"final_state": "inbox",
"warnings": [
"Downgraded to inbox: Safe Mode ON (workspace-level governance)"
],
"tokens_used": { "prompt": 1234, "completion": 567 }
}
Mixed (partial failure)
{
"run_id": "uuid",
"scheduled_post_ids": ["uuid1", "uuid2"],
"inbox_post_ids": ["uuid3"],
"inbox_item_id": "bundle-uuid",
"final_state": "mixed",
"warnings": [
"Post 3: schedule_time missing, routed to inbox"
],
"tokens_used": { "prompt": 1234, "completion": 567 }
}
Failure Handling
Same as bolta.cron.generate_to_review:
- Voice deleted → pause job
- Account disconnected → pause job
- Template rendering fails → fall back to voice generation
- Max retries exceeded → create inbox item with error context
Additional (scheduling-specific):
- Invalid schedule_time → route that post to inbox, continue with others
- Platform API scheduling fails → route to inbox, add warning
- Missing schedule_times → route to inbox with suggestion to add times or use memory
When to Use This Skill (User Guidance)
Use bolta.cron.generate_to_review when:
- First setting up automation (test before trusting)
- High-sensitivity brands (every post needs eyes)
- New voice profiles (not proven yet)
- Experimental content strategies
Use bolta.cron.generate_and_schedule when:
- Agent proven reliable (reviewed 50+ posts, 95%+ approval rate)
- Safe Mode OFF (workspace decision)
- Editor/Admin role agents (trusted with scheduling)
- High-volume automation (24/7 content flow)
- Agency autopilot mode (30+ clients, can't manually review everything)
Migration path:
- Start with
generate_to_review(Safe Mode ON) - Review agent output for 1-2 weeks
- If quality high: turn Safe Mode OFF, switch job to
generate_and_schedule - Monitor audit logs; revert if quality drops
Agent Types That Use This Skill
- content_creator — Primary use case (autopilot content generation)
- custom — User-defined agents with full automation enabled
Example Job Configuration
{
"id": "job-uuid",
"agent_id": "trusted-creator-uuid",
"name": "Daily LinkedIn Autopilot",
"voice_profile_id": "bolta-voice-uuid",
"account_ids": ["linkedin-uuid"],
"schedule": {"cron": "0 10 * * 1-5"},
"trigger": "scheduled",
"status": "active",
"n_posts": 1,
"schedule_times": ["10:00"],
"client_tag": "Acme Corp",
"run_instructions": "Professional thought-leadership posts. Balance education vs promotion 70/30."
}
When Celery fires this job at 10am on weekdays:
- Checks Safe Mode (OFF) + agent role (Editor) → execution_mode = "schedule"
- Generates 1 post using voice + memory
- Schedules for 10:00am same day
- Post goes live at 10:00am without human intervention
- Human reviews retrospectively via audit logs