Skip to content

AI Workforce -- PRD

Product: Objectuve (codename: Enkidu) Feature: In-house agentic AI employee team for business operations Status: Shipped — April 2026 Date: April 2026


1. Problem Statement

Objectuve is operated by a lean team spanning product, brand, marketing, content, social, SEO, analytics, and customer support. Each discipline consumes time that doesn't compound the product itself. The discovery analysis (docs/operations/ai-employees-discovery.md) evaluated external vendors (Sintra, Lindy, Gumloop) and concluded none of them fit:

Three problems:

  1. Domain expertise exists but can't execute autonomously. The Claude Code harness contains ~40 specialized skills (.claude/skills/) covering nearly every business operation -- crafting-page-messaging, designing-lifecycle-messages, orchestrating-social-rhythm, inspecting-search-coverage, and more. Each encodes Objectuve-specific constraints: brand voice, anti-social philosophy, stack conventions, legal entity details. What's missing is not knowledge. It's scheduling, memory, integration, and oversight -- the ability to run on a cadence, remember past outputs, ship artifacts to the right destination, and stay accountable to a human reviewer.

  2. No vendor can leverage the existing skill library. External platforms wrap commodity LLMs in persona wrappers. They can't read .claude/skills/, can't enforce the "coach who's also a friend" brand voice at the prompt level, and can't integrate with Objectuve's stack (PostHog, Sentry, Mailtrap, Clerk, Cloud Run). Every vendor requires rebuilding the domain expertise from scratch -- and maintaining it in a foreign DSL.

  3. Repetitive operational tasks are blocking product work. Weekly funnel digests, social cadence planning, SEO audits, lifecycle email drafts, and first-response support all follow predictable patterns. A human must currently initiate each task, provide context, review output, and ship it manually. This overhead scales linearly with operational scope and competes directly with engineering time.

AI Workforce solves this by wiring the existing skill library into autonomous agents that run on schedule, produce reviewable artifacts, and ship approved outputs to their destinations -- all built in-house on the Claude Agent SDK, riding on the LiteLLM proxy already in the stack.


2. Goals & Success Metrics

Primary Goals

  1. Build a reusable platform for defining, scheduling, executing, and overseeing AI employees
  2. Ship 5 employees in shadow mode (human approves every artifact before it ships)
  3. Reduce founder operational overhead by 8-12 hours/week within 90 days
  4. Maintain brand-voice fidelity by loading Objectuve's skill library into every employee's context

Success Metrics

MetricTarget (90 days post-launch)Measurement
Shadow-mode artifact approval rate>60% ship with <10% editsEdit distance on approved vs. generated content
Weekly artifacts produced25+ across all 5 employeesAiRun count where status = succeeded
Founder hours saved per week8-12 hrsSelf-reported time tracking, pre/post comparison
Cost per artifact<$0.10 averageLiteLLM usage tracking / artifact count
Platform uptime99.5%+Agent Runner health check + Sentry error rate
Time from run trigger to artifact ready<5 minutes for most employeesAiRun duration (started_at to finished_at)

Guardrails

  • Shadow mode is mandatory for all employees at launch. No auto-publishing until an employee demonstrates >80% approval rate over 4+ weeks.
  • No customer-facing autonomous output. Customer support drafts require human review and send, with no exceptions, through all phases.
  • Monthly cost cap per employee. Hard stop at budget threshold via LiteLLM. Default: $30/employee/month.
  • Brand-voice post-filter. Every user-facing artifact passes through the tightening-brand-voice skill as a mandatory output validation step.
  • No access to production user data. Employees read aggregated analytics (PostHog), error patterns (Sentry), and public content -- never individual user records, PII, or authentication credentials.

3. User Stories

  • US-1: As an operator, I want to define an AI employee with a name, role, skill set, and schedule, so it runs automatically without me initiating each task.
  • US-2: As an operator, I want to see all pending artifacts in a review queue, so I can approve, edit, or reject each one before it ships.
  • US-3: As an operator, I want each employee to remember context from previous runs (last post topics, engagement data, audit findings), so outputs improve over time and don't repeat themselves.
  • US-4: As an operator, I want to trigger an employee run manually outside its schedule, so I can get ad-hoc outputs when needed.
  • US-5: As an operator, I want to see cost and token usage per employee per month, so I can manage the AI budget.
  • US-6: As an operator, I want to pause or disable an employee without deleting its configuration or memory, so I can temporarily stop runs during quiet periods.
  • US-7: As an operator, I want approved content artifacts to be automatically delivered to their destination (PR opened, email queued, report posted to Slack), so I don't have to manually copy-paste.
  • US-8: As an operator, I want to see a run history with status, duration, cost, and output for each employee, so I can audit performance and catch regressions.
  • US-9: As an operator, I want to configure which LLM model an employee uses (Claude, Gemini, GPT), so I can optimize for cost vs. quality per role.
  • US-10: As an operator, I want to receive Slack notifications when an artifact is ready for review, so I don't have to poll the dashboard.

4. Feature Design

4a. Employee Definitions (The Roster)

Each employee is defined by a role, a set of loaded skills, available tools (MCP servers), a trigger pattern, and output type. All start in shadow mode (artifacts queued for human approval).


Employee 1: Cori -- Content Drafter

Role: Generates marketing copy, blog post drafts, and feature release narratives aligned with Objectuve's brand voice.

Skills loaded:

  • crafting-page-messaging (primary)
  • framing-release-stories
  • tightening-brand-voice (mandatory post-filter)

Tools (MCP):

  • GitHub MCP (read marketing_landing/, CHANGELOG.md, docs/)
  • File system read (skill definitions, brand guide)

Trigger: Weekly (Monday 9 AM) + manual Output type: draft_post -- markdown content with metadata (target page, section, word count) Delivery on approval: Opens a PR against marketing_landing/ or docs/ with the drafted content

Example run: "Review the current hero section of marketing_landing/index.html. Draft an updated version that reflects the latest shipped features from CHANGELOG.md. Apply brand voice constraints. Output as a PR diff."


Employee 2: Emi -- Lifecycle Email Designer

Role: Drafts onboarding welcome series, re-engagement emails, milestone celebration emails, and streak recovery nudges.

Skills loaded:

  • designing-lifecycle-messages (primary)
  • crafting-page-messaging
  • tightening-brand-voice (mandatory post-filter)

Tools (MCP):

  • Mailtrap API (list templates, create draft)
  • GitHub MCP (read email templates in rails_api/app/views/)
  • PostHog MCP (read activation funnel drop-off data)

Trigger: Bi-weekly (1st and 15th, 10 AM) + manual Output type: email_template -- subject line, preheader, HTML body, plain-text fallback Delivery on approval: Creates draft in Mailtrap (not sent until operator triggers send)

Example run: "Analyze PostHog activation funnel. Identify the step with highest drop-off. Draft a re-engagement email targeting users who stalled at that step. Include a coach-tone subject line, a single CTA, and unsubscribe footer."


Employee 3: Scout -- SEO Auditor

Role: Weekly audit of marketing landing meta tags, Schema.org markup, sitemap completeness, and Open Graph tags. Opens PRs with fixes.

Skills loaded:

  • inspecting-search-coverage (primary)
  • adding-structured-signals
  • scaling-template-pages

Tools (MCP):

  • GitHub MCP (read/write marketing_landing/)
  • Web fetch (check live meta tags, validate OG rendering)

Trigger: Weekly (Wednesday 8 AM) + manual Output type: code_patch -- diff against marketing_landing/ with audit findings Delivery on approval: Opens a PR with SEO fixes and an audit summary in the PR description

Example run: "Crawl marketing_landing/index.html. Verify all meta tags are present and correct. Check Schema.org JSON-LD matches current product description. Validate Open Graph tags render correctly. Generate a PR with any fixes needed."


Employee 4: Dash -- Analytics Reporter

Role: Produces weekly funnel digests, activation metric summaries, and engagement trend reports.

Skills loaded:

  • instrumenting-product-metrics (primary)
  • mapping-conversion-events
  • running-product-experiments

Tools (MCP):

  • PostHog MCP (read funnels, trends, cohorts, events)
  • Sentry MCP (read error counts, issue trends)

Trigger: Weekly (Friday 8 AM) + manual Output type: report -- structured markdown with metrics tables, trend arrows, and recommendations Delivery on approval: Posts to a designated Slack channel + saves to docs/reports/

Example run: "Pull this week's activation funnel from PostHog (signup -> first goal -> first check-in -> 7-day retention). Compare to last week. Highlight any step with >5% regression. Include Sentry error count delta. Recommend one action item."


Employee 5: Ally -- Customer Support Drafter

Role: Drafts first-response templates for common support patterns. Maintains FAQ content. Never sends directly -- always human-reviewed.

Skills loaded:

  • refining-prompt-surfaces (primary)
  • tightening-brand-voice (mandatory post-filter)
  • crafting-empty-states

Tools (MCP):

  • Sentry MCP (read recent error patterns, user-reported issues)
  • GitHub MCP (read docs/, existing FAQ content)

Trigger: Daily (7 AM) + manual Output type: recommendation -- response draft with context summary, suggested tone, and related docs Delivery on approval: Saved to support response library (never auto-sent)

Example run: "Review Sentry issues from the last 24 hours. For any issue affecting >5 users, draft a customer-facing response acknowledging the issue, explaining the impact, and providing a workaround if available. Use coach-friend tone."


4b. Autonomy Levels

Every employee progresses through three levels. Promotion requires evidence.

LevelBehaviorPromotion criteria
ShadowRuns on schedule, produces artifacts, all artifacts require human approval before delivery.Default for all new employees.
Semi-autonomousLow-risk artifacts auto-approve within guardrails (e.g., SEO fixes under 10 lines, reports to Slack). High-risk artifacts still require approval.>80% approval rate over 4+ weeks. Zero rejected artifacts in last 2 weeks.
AutonomousRuns and delivers without blocking on approval. Oversight shifts to anomaly detection (cost spikes, output length anomalies, brand-voice drift).>95% approval rate over 8+ weeks. Founder explicitly promotes. Customer support is never eligible.

4c. Memory System

Each employee maintains persistent memory across runs via AiEmployeeMemory records. Memory is key-value JSONB, scoped per employee.

Standard memory keys:

KeyPurposeExample
last_run_summaryWhat the employee did last time"Generated 3 blog post drafts, 2 approved, 1 rejected (too promotional)"
running_contextAccumulated context that carries forward"Current editorial theme: Summer 2026 launch. Last 3 posts covered: streaks, coach personas, community challenges."
performance_statsApproval rate, edit distance, rejection reasons{ approval_rate: 0.82, avg_edit_distance: 0.08, common_rejections: ["too long", "off-brand"] }
domain_stateRole-specific stateFor SEO Auditor: { last_audit_findings: [...], fixed_issues: [...], known_false_positives: [...] }

Memory is loaded into the agent's system prompt at the start of each run and updated after the run completes. Memory records are append-only (soft-deleted, never overwritten) for audit trail.

4d. Approval Workflow

Run completes


Artifact created (status: pending)

    ├─── Slack notification to operator


Admin Dashboard: AI Workforce > Review Queue

    ├── Approve → artifact.delivery_method triggers
    │   ├── draft_post → GitHub PR opened
    │   ├── email_template → Mailtrap draft created
    │   ├── code_patch → GitHub PR opened
    │   ├── report → Slack post + docs/ save
    │   └── recommendation → saved to support library

    ├── Edit + Approve → operator modifies payload, then delivery triggers
    │   (edit distance tracked for performance metrics)

    └── Reject → artifact marked rejected, rejection reason stored
        (feeds back into employee memory for future improvement)

4e. Skill Loading Architecture

Each .claude/skills/SKILL.md file is loaded as a system prompt fragment when an employee starts a run. The Agent Runner:

  1. Reads the employee's skill_refs array (e.g., ["crafting-page-messaging", "tightening-brand-voice"])
  2. Loads each skill file from .claude/skills/{name}/SKILL.md
  3. Strips YAML frontmatter, injects the markdown body into the agent's system prompt
  4. Appends the employee's memory context
  5. Appends the run-specific task prompt

The tightening-brand-voice skill is applied as a post-filter: after the agent produces output, a second LLM call validates the output against brand constraints and flags any violations. This is a hard gate -- artifacts with brand-voice violations are auto-rejected with a reason.

4f. Agent Runner Service

A standalone TypeScript service deployed on Cloud Run, using the Claude Agent SDK.

Endpoints:

EndpointMethodPurpose
/runsPOSTTrigger a new run for an employee
/runs/:id/statusGETCheck run status
/runs/:id/cancelPOSTCancel a running agent
/healthGETHealth check for Cloud Run

Request flow:

  1. Rails enqueues AiWorkforce::ExecuteRunJob via Sidekiq (on schedule or manual trigger)
  2. The job creates an AiRun record (status: queued) and POSTs to the Agent Runner
  3. Agent Runner loads employee config, skills, memory, and tools
  4. Agent executes using Claude Agent SDK (multi-step tool use)
  5. On completion, Agent Runner POSTs results back to Rails via webhook (/webhooks/ai-workforce)
  6. Rails creates AiArtifact records from the results, updates AiRun status
  7. Slack notification fires if artifacts are pending approval

Authentication: OIDC service-to-service auth (same pattern as LiteLLM proxy). Agent Runner and Rails API both run on Cloud Run with IAM-managed identity tokens.

MCP Server Configuration: Each employee definition includes an mcp_servers array specifying which MCP servers the agent can access during its run. The Agent Runner spins up the specified MCP connections at run start and tears them down after.

4g. Admin Dashboard Views

New views added to the existing admin dashboard at admin_dashboard/.

1. AI Workforce Overview (/ai-workforce)

  • Card grid showing each employee: name, role, status (active/paused), autonomy level, next scheduled run, last run result, monthly cost
  • Quick actions: trigger run, pause/resume, view history

2. Review Queue (/ai-workforce/review)

  • Table of pending artifacts sorted by creation time
  • Each row: employee name, artifact type, preview snippet, created at, action buttons (approve/edit/reject)
  • Slide-over panel for full artifact preview with diff view (for code patches) or rich text preview (for content/emails)
  • Bulk approve for low-risk artifact types

3. Employee Detail (/ai-workforce/:id)

  • Employee config: name, role, skills, model, schedule, autonomy level
  • Run history table: date, duration, status, cost, artifact count
  • Memory viewer: current memory state with expandable JSON
  • Performance chart: approval rate over time, cost trend, output volume

4. Run Detail (/ai-workforce/runs/:id)

  • Full run log: steps taken, tools invoked, tokens used, duration
  • Artifacts produced with approval status
  • Error details (if failed)

5. Technical Approach

5a. Data Model

All models inherit from PublicRecord and use acts_as_paranoid.

ruby
# rails_api/app/models/ai_employee.rb
class AiEmployee < PublicRecord
  acts_as_paranoid

  has_many :ai_runs, dependent: :destroy
  has_many :ai_employee_memories, dependent: :destroy

  # Validations
  validates :name, presence: true
  validates :role_key, presence: true, uniqueness: true
  validates :autonomy_level, inclusion: { in: %w[shadow semi_autonomous autonomous] }
  validates :monthly_budget_cents, numericality: { greater_than: 0 }

  # Columns:
  # - name (string) — "Cori, Content Drafter"
  # - role_key (string, unique) — "content_drafter"
  # - description (text) — role summary
  # - skill_refs (string array) — ["crafting-page-messaging", "tightening-brand-voice"]
  # - mcp_servers (string array) — ["github", "posthog"]
  # - model_preference (string) — LiteLLM model alias, e.g. "claude-sonnet-4-5"
  # - schedule_cron (string, nullable) — cron expression, null = manual only
  # - autonomy_level (string) — "shadow" | "semi_autonomous" | "autonomous"
  # - active (boolean, default: true)
  # - monthly_budget_cents (integer, default: 3000) — $30
  # - task_prompt (text) — the recurring task instruction
  # - post_filter_skill (string, nullable) — e.g. "tightening-brand-voice"

  scope :active, -> { where(active: true) }
  scope :scheduled, -> { active.where.not(schedule_cron: nil) }
end
ruby
# rails_api/app/models/ai_run.rb
class AiRun < PublicRecord
  acts_as_paranoid

  belongs_to :ai_employee
  has_many :ai_artifacts, dependent: :destroy

  # Columns:
  # - ai_employee_id (bigint, FK)
  # - triggered_by (string) — "schedule" | "manual" | "webhook"
  # - status (string) — "queued" | "running" | "succeeded" | "failed" | "cancelled"
  # - started_at (datetime)
  # - finished_at (datetime)
  # - prompt_tokens (integer)
  # - completion_tokens (integer)
  # - cost_cents (decimal)
  # - error_message (text, nullable)
  # - run_log (jsonb) — step-by-step execution trace
  # - memory_snapshot (jsonb) — memory state at run start (for debugging)

  scope :recent, -> { order(created_at: :desc) }
  scope :pending_review, -> { joins(:ai_artifacts).where(ai_artifacts: { approval_status: 'pending' }).distinct }
end
ruby
# rails_api/app/models/ai_artifact.rb
class AiArtifact < PublicRecord
  acts_as_paranoid

  belongs_to :ai_run

  # Columns:
  # - ai_run_id (bigint, FK)
  # - kind (string) — "draft_post" | "email_template" | "code_patch" | "report" | "recommendation"
  # - title (string) — human-readable artifact name
  # - payload (jsonb) — content, metadata, file paths, diffs
  # - approval_status (string) — "pending" | "approved" | "rejected" | "auto_approved"
  # - rejection_reason (text, nullable)
  # - edit_distance (float, nullable) — 0.0-1.0, how much the operator changed it
  # - reviewed_by (bigint, FK to users, nullable)
  # - reviewed_at (datetime, nullable)
  # - delivered_at (datetime, nullable) — when the artifact was shipped to its destination
  # - delivery_method (string) — "github_pr" | "mailtrap_draft" | "slack_post" | "file_save" | "none"
  # - delivery_metadata (jsonb, nullable) — PR URL, Slack message ID, etc.

  scope :pending, -> { where(approval_status: 'pending') }
  scope :approved, -> { where(approval_status: %w[approved auto_approved]) }
end
ruby
# rails_api/app/models/ai_employee_memory.rb
class AiEmployeeMemory < PublicRecord
  acts_as_paranoid

  belongs_to :ai_employee

  # Columns:
  # - ai_employee_id (bigint, FK)
  # - key (string) — "last_run_summary", "running_context", "performance_stats", "domain_state"
  # - value (jsonb)

  validates :key, presence: true, uniqueness: { scope: :ai_employee_id }
end

5b. Agent Runner Service

Location: agent_runner/ in the monorepo (new top-level directory, alongside ionic_frontend/, rails_api/, admin_dashboard/)

Stack:

  • Node.js 20+ / TypeScript
  • Claude Agent SDK (@anthropic-ai/agent-sdk)
  • Express (HTTP server for webhook endpoints)
  • MCP client libraries for tool access

Key files:

FilePurpose
agent_runner/src/server.tsExpress app with /runs, /health endpoints
agent_runner/src/runner.tsCore agent execution loop using Claude Agent SDK
agent_runner/src/skills.tsSkill loader: reads SKILL.md files, strips frontmatter, returns prompt fragments
agent_runner/src/memory.tsMemory loader/updater: fetches from Rails API, posts updates back
agent_runner/src/delivery.tsArtifact delivery handlers (GitHub PR, Mailtrap, Slack)
agent_runner/src/mcp/MCP server connection configs per integration
agent_runner/DockerfileCloud Run container image
agent_runner/package.jsonDependencies

Environment variables:

VariablePurpose
ANTHROPIC_API_KEYClaude API access (or route through LiteLLM)
LITELLM_URLLiteLLM proxy for model routing + cost tracking
RAILS_API_URLCallback URL for posting results
GITHUB_TOKENGitHub MCP: open PRs, read repo
POSTHOG_API_KEYPostHog MCP: read analytics
SENTRY_AUTH_TOKENSentry MCP: read errors
MAILTRAP_API_TOKENMailtrap: create email drafts
SLACK_WEBHOOK_URLSlack: post reports and notifications
SKILLS_PATHPath to .claude/skills/ directory (mounted or fetched)

5c. Rails Interactions

ruby
# app/interactions/ai_workforce/trigger_run.rb
class AiWorkforce::TriggerRun < Interaction::Base
  delegate_input :employee_id, :triggered_by

  def call
    employee = AiEmployee.active.public_find(employee_id)
    return result.fail(error: 'Employee not found or inactive') unless employee

    # Check monthly budget
    month_spend = employee.ai_runs
      .where('created_at >= ?', Time.current.beginning_of_month)
      .sum(:cost_cents)
    if month_spend >= employee.monthly_budget_cents
      return result.fail(error: 'Monthly budget exceeded', code: :budget_exceeded)
    end

    run = employee.ai_runs.create!(
      triggered_by: triggered_by,
      status: 'queued'
    )

    # Enqueue the actual execution
    AiWorkforce::ExecuteRunJob.perform_async(run.public_id)

    result.details = { run_id: run.public_id }
  end
end
ruby
# app/interactions/ai_workforce/approve_artifact.rb
class AiWorkforce::ApproveArtifact < Interaction::Base
  delegate_input :artifact_id, :reviewer_id, :edited_payload

  def call
    artifact = AiArtifact.pending.public_find(artifact_id)
    reviewer = User.public_find(reviewer_id)

    # Calculate edit distance if payload was modified
    edit_distance = if edited_payload.present?
      calculate_edit_distance(artifact.payload, edited_payload)
    else
      0.0
    end

    artifact.update!(
      approval_status: 'approved',
      reviewed_by: reviewer.id,
      reviewed_at: Time.current,
      edit_distance: edit_distance,
      payload: edited_payload || artifact.payload
    )

    # Trigger delivery
    AiWorkforce::DeliverArtifactJob.perform_async(artifact.public_id)

    # Update employee memory with approval feedback
    AiWorkforce::UpdateEmployeeMemory.call(
      employee_id: artifact.ai_run.ai_employee.public_id,
      key: 'performance_stats',
      event: :approved,
      edit_distance: edit_distance
    )

    result.details = { artifact_id: artifact.public_id }
  end
end

5d. GraphQL Types & Mutations

New types:

TypeLocationFields
AiEmployeeTypeapp/graphql/types/ai_employee_type.rbname, roleKey, description, skillRefs, autonomyLevel, active, scheduleCron, monthlyBudgetCents, currentMonthCost, lastRun, nextRunAt
AiRunTypeapp/graphql/types/ai_run_type.rbemployee, triggeredBy, status, startedAt, finishedAt, promptTokens, completionTokens, costCents, artifacts, errorMessage
AiArtifactTypeapp/graphql/types/ai_artifact_type.rbkind, title, payload, approvalStatus, rejectionReason, editDistance, reviewedBy, deliveredAt, deliveryMethod
AiEmployeeMemoryTypeapp/graphql/types/ai_employee_memory_type.rbkey, value, updatedAt

New mutations:

MutationPurpose
TriggerAiRunManually trigger a run for an employee
ApproveAiArtifactApprove an artifact (with optional edited payload)
RejectAiArtifactReject an artifact with a reason
UpdateAiEmployeeUpdate employee config (schedule, model, autonomy level, active)
PauseAiEmployeeToggle active status

New queries:

QueryPurpose
aiEmployeesList all employees with status and stats
aiEmployee(id:)Single employee with full detail
pendingArtifactsAll artifacts awaiting review
aiRuns(employeeId:, limit:)Run history for an employee

5e. Sidekiq Jobs

JobQueuePurpose
AiWorkforce::ExecuteRunJobai_workforcePOSTs run request to Agent Runner service
AiWorkforce::DeliverArtifactJobai_workforceDelivers approved artifact to destination (GitHub PR, Mailtrap, Slack)
AiWorkforce::ScheduleRunsJobai_workforceCrono cron: checks all scheduled employees, enqueues due runs
AiWorkforce::BudgetAlertJoblowCrono cron: checks monthly spend per employee, alerts at 80%/100%

5f. Admin Dashboard Integration

New views added to admin_dashboard/src/views/:

FileRoutePurpose
AIWorkforceView.vue/ai-workforceEmployee overview grid
AIReviewQueue.vue/ai-workforce/reviewPending artifact approval queue
AIEmployeeDetail.vue/ai-workforce/:idSingle employee detail, history, memory
AIRunDetail.vue/ai-workforce/runs/:idSingle run detail with execution log

New GraphQL operations in admin_dashboard/src/graphql/:

FileOperations
ai_workforce_queries.tsGET_AI_EMPLOYEES, GET_AI_EMPLOYEE, GET_PENDING_ARTIFACTS, GET_AI_RUNS, GET_AI_RUN
ai_workforce_mutations.tsTRIGGER_AI_RUN, APPROVE_ARTIFACT, REJECT_ARTIFACT, UPDATE_AI_EMPLOYEE, PAUSE_AI_EMPLOYEE

Navigation update in AdminLayout.vue: add "AI Workforce" nav item with CpuChipIcon from Heroicons.


6. Implementation Phases

Phase 1: Platform Infrastructure (2-3 weeks)

Backend:

  1. Database migrations: ai_employees, ai_runs, ai_artifacts, ai_employee_memories
  2. Models with validations, associations, scopes
  3. Interactions: TriggerRun, ApproveArtifact, RejectArtifact, UpdateAiEmployee
  4. GraphQL types and mutations
  5. Sidekiq jobs: ExecuteRunJob, DeliverArtifactJob, ScheduleRunsJob
  6. Webhook endpoint: POST /webhooks/ai-workforce for Agent Runner callbacks
  7. Seed data: 5 employee definitions

Agent Runner: 8. Scaffold TypeScript service with Express 9. Core runner loop using Claude Agent SDK 10. Skill loader (read .claude/skills/, strip frontmatter) 11. Health check endpoint 12. Dockerfile + Cloud Run deploy config

Admin Dashboard: 13. AI Workforce overview page (employee cards) 14. Review queue page (approve/reject) 15. Navigation update

NOT in Phase 1: Memory system, delivery automation, Slack notifications, employee detail page, run detail page.

Phase 2: Shadow Mode -- 5 Employees (2-3 weeks)

  1. Connect Agent Runner to LiteLLM proxy (model routing, cost tracking)
  2. MCP server integrations: GitHub, PostHog, Sentry, Mailtrap
  3. Wire all 5 employees with their skill sets and task prompts
  4. Memory system: load at run start, update after run
  5. Brand-voice post-filter pipeline
  6. Employee detail page with run history
  7. Run detail page with execution log
  8. Slack notifications for pending artifacts
  9. Crono scheduled execution for all 5 employees

NOT in Phase 2: Auto-approval, delivery automation, autonomous mode.

Phase 3: Delivery Automation (1-2 weeks)

  1. GitHub PR delivery handler (Content Drafter + SEO Auditor)
  2. Mailtrap draft delivery handler (Lifecycle Email Designer)
  3. Slack post delivery handler (Analytics Reporter)
  4. Support library save handler (Customer Support Drafter)
  5. Delivery status tracking (delivered_at, delivery_metadata)
  6. Budget alert job (monthly spend per employee)

Phase 4: Semi-Autonomous Graduation (after 4+ weeks of shadow data)

  1. Auto-approval rules engine (per employee, per artifact type, configurable thresholds)
  2. Anomaly detection: flag runs with unusual cost, duration, or output length
  3. Performance dashboard: approval rate trends, edit distance over time
  4. Autonomy level promotion UI in admin dashboard

7. Out of Scope

  • Social media posting. Platform API integrations (Twitter/X, LinkedIn, Instagram) are complex OAuth flows. Deferred to a future PRD. Employees can draft social content, but posting requires manual copy-paste or a future Buffer/Typefully integration.
  • Consumer-facing AI changes. The AI Workforce is purely operational. It does not affect the user-facing AI Coach, check-in prompts, or insight cards.
  • Multi-agent orchestration. A "manager agent" that delegates to "employee agents" in a single run is architecturally possible with Claude Agent SDK but deferred. Each employee runs independently in v1.
  • Custom employee creation UI. Employees are defined in seed data and configured via the admin dashboard. A full "create employee" wizard is Phase 5+.
  • Vector-based memory / RAG. Simple JSONB key-value memory is sufficient for v1. Embedding-based retrieval is premature optimization.
  • External vendor integrations. No Gumloop, Zapier, Sintra, or other agent platforms. Fully in-house.

8. Decisions (Resolved)

  1. Claude Agent SDK over CrewAI/LangGraph. Same mental model as Claude Code (already the daily driver). Tool-use-first design maps directly to MCP servers. Agents-invoke-agents is a first-class primitive. TypeScript matches the frontend ecosystem. CrewAI is Python-only; LangGraph is over-engineered for the current scope.

  2. Separate TypeScript service over Ruby shim. Claude Agent SDK is TypeScript-native. Wrapping it in Ruby via HTTP calls to the Claude API would lose SDK features (tool orchestration, MCP connections, agent delegation). A separate Cloud Run service is clean, independently scalable, and follows the same pattern as the LiteLLM proxy.

  3. Skills as system prompt fragments over fine-tuning. Loading .claude/skills/ as prompt context preserves the ability to edit skills and see changes on the next run. Fine-tuning would require retraining after every skill update and would lose the explicit, auditable constraint definitions.

  4. Shadow mode mandatory over opt-in. The discovery doc's risk analysis cited documented hallucination incidents (Medvi's fabricated drug prices). Every employee starts in shadow mode with no exceptions. Promotion to semi-autonomous requires evidence.

  5. JSONB memory over vector embeddings. Employees have small, structured memory needs (last run summary, domain state, performance stats). JSONB with explicit keys is simpler, queryable with standard SQL, and doesn't require a vector database. Revisit if memory exceeds 100KB per employee.

  6. Per-employee budget caps over shared pool. Individual caps prevent one runaway employee from consuming the entire budget. Default $30/employee/month ($150 total) is well within the discovery doc's $40-$150 estimate.

  7. Admin dashboard integration over standalone UI. The admin dashboard already exists with Clerk auth, Apollo GraphQL, and Tailwind patterns. Adding 4 views is faster than building a new app.


9. Edge Cases & Open Questions

Edge Cases

#ScenarioResolution
EC-1Agent Runner is down when a scheduled run firesSidekiq retries with exponential backoff (3 retries). AiRun stays queued. Alert after 3 failures.
EC-2Agent exceeds token limit mid-runClaude Agent SDK handles context window limits. If a run hits the limit, it fails gracefully. AiRun status set to failed with error.
EC-3Approved artifact delivery fails (GitHub API down, Mailtrap error)DeliverArtifactJob retries 3x. If still failing, artifact stays approved but delivered_at is null. Slack alert to operator.
EC-4Employee's skills are deleted or renamedSkill loader logs a warning and continues with available skills. AiRun log includes "skill not found: {name}". Employee still runs with partial context.
EC-5Two scheduled runs for the same employee overlapMutex: ExecuteRunJob checks for an existing running AiRun. If found, skips with a log entry. No concurrent runs per employee.
EC-6Operator edits payload then approves, but edit breaks delivery formatPayload schema validation before delivery. If invalid, artifact stays approved but delivery fails with a descriptive error.
EC-7Monthly budget reached mid-runThe budget check is pre-run only. A run that starts under budget completes even if it exceeds the cap. Post-run cost tracking updates the total and alerts.
EC-8Brand-voice post-filter rejects outputArtifact is auto-rejected with reason "brand-voice violation: {details}". Counts against approval rate but not against the operator's review burden.

Open Questions

  1. Should the Agent Runner route LLM calls through LiteLLM or call Anthropic directly? Recommend LiteLLM -- unified cost tracking, model routing, and fallbacks already exist. The Agent Runner becomes another LiteLLM client with its own model aliases (e.g., workforce/content, workforce/support).

  2. How should skills be made available to the Agent Runner on Cloud Run? Options: (a) mount the repo directory, (b) fetch from GitHub API at startup, (c) bundle into the Docker image at build time. Recommend (c) -- simplest, skills change infrequently, and a new deploy picks up changes.

  3. Should employees share a Slack channel or each post to their own? Recommend one shared #ai-workforce channel with employee name prefixes. Separate channels creates notification fatigue.

  4. XP/badge award for the operator who reviews artifacts? The existing gamification system could award XP for artifact reviews, creating an "AI Wrangler" badge. Deferred to Phase 4 but worth noting.

  5. Should the Agent Runner service live in the same GCP project? Recommend yes -- same project (enkidu-488723), same region (us-central1), IAM-managed auth. Keeps networking simple and costs consolidated.


Critical Files

FileChange
rails_api/db/migrate/YYYYMMDD_create_ai_employees.rbMigration: ai_employees table
rails_api/db/migrate/YYYYMMDD_create_ai_runs.rbMigration: ai_runs table
rails_api/db/migrate/YYYYMMDD_create_ai_artifacts.rbMigration: ai_artifacts table
rails_api/db/migrate/YYYYMMDD_create_ai_employee_memories.rbMigration: ai_employee_memories table
rails_api/app/models/ai_employee.rbAiEmployee model
rails_api/app/models/ai_run.rbAiRun model
rails_api/app/models/ai_artifact.rbAiArtifact model
rails_api/app/models/ai_employee_memory.rbAiEmployeeMemory model
rails_api/app/interactions/ai_workforce/trigger_run.rbRun trigger interaction
rails_api/app/interactions/ai_workforce/approve_artifact.rbArtifact approval interaction
rails_api/app/interactions/ai_workforce/reject_artifact.rbArtifact rejection interaction
rails_api/app/interactions/ai_workforce/update_employee_memory.rbMemory update interaction
rails_api/app/graphql/types/ai_employee_type.rbGraphQL type
rails_api/app/graphql/types/ai_run_type.rbGraphQL type
rails_api/app/graphql/types/ai_artifact_type.rbGraphQL type
rails_api/app/graphql/mutations/ai_workforce/All workforce mutations
rails_api/app/jobs/ai_workforce/execute_run_job.rbSidekiq: trigger Agent Runner
rails_api/app/jobs/ai_workforce/deliver_artifact_job.rbSidekiq: deliver approved artifacts
rails_api/app/jobs/ai_workforce/schedule_runs_job.rbCrono: check cron schedules
rails_api/app/controllers/webhooks/ai_workforce_controller.rbWebhook for Agent Runner callbacks
agent_runner/src/server.tsAgent Runner Express server
agent_runner/src/runner.tsCore agent execution using Claude Agent SDK
agent_runner/src/skills.tsSkill loader
agent_runner/src/memory.tsMemory loader/updater
agent_runner/src/delivery.tsArtifact delivery handlers
agent_runner/src/mcp/MCP server configurations
agent_runner/DockerfileCloud Run container
admin_dashboard/src/views/AIWorkforceView.vueEmployee overview
admin_dashboard/src/views/AIReviewQueue.vueArtifact approval queue
admin_dashboard/src/views/AIEmployeeDetail.vueEmployee detail + history
admin_dashboard/src/views/AIRunDetail.vueRun detail + execution log
admin_dashboard/src/graphql/ai_workforce_queries.tsGraphQL queries
admin_dashboard/src/graphql/ai_workforce_mutations.tsGraphQL mutations
admin_dashboard/src/layouts/AdminLayout.vueAdd AI Workforce nav item
infra/litellm/config.production.yamlAdd workforce model aliases
.claude/skills/tightening-brand-voice/SKILL.mdReferenced by brand-voice post-filter

Last updated: 2026-07-15

Loading…