{
  "id": "2026-04-27-traci-sdr-design-20fb4b9b99",
  "scope": "redkey",
  "source_of_truth": "repo",
  "source_path": "docs/specs/2026-04-27-traci-sdr-design.md",
  "source_kind": "markdown",
  "visibility": "internal",
  "renderer_id": "design_doc.dreamborn-forge.generated.v1",
  "design_system": "dreamborn-design-system:forge",
  "generated_at": "2026-05-09T13:00:55.684Z",
  "artifact_type": "design_doc",
  "schema_version": "design_doc.generated.v1",
  "title": "Traci — DreamBorn SDR Agent Design Spec",
  "summary": "Traci — DreamBorn SDR Agent Design Spec Date: 2026 04 27 Client: dreamborn Status: Design approved — ready for planning Overview Traci is DreamBorn's outbound SDR agent. Her job is to build a prospect pool on LinkedIn and warm it toward a deal definition call with Justin. She does not close — she hands off to Brooke (Sales) once a meeting is booked. Revenue ...",
  "format_source": "markdown",
  "sections": [
    {
      "title": "Traci — DreamBorn SDR Agent Design Spec",
      "level": 1,
      "body": "**Date:** 2026-04-27\n**Client:** dreamborn\n**Status:** Design approved — ready for planning\n\n---"
    },
    {
      "title": "Overview",
      "level": 2,
      "body": "Traci is DreamBorn's outbound SDR agent. Her job is to build a prospect pool on LinkedIn and warm it toward a deal-definition call with Justin. She does not close — she hands off to Brooke (Sales) once a meeting is booked.\n\n**Revenue pipeline:** Traci → Justin (call) → Brooke (close) → Annie (CS)\n\n**Phase:** Connection + nurture campaign. Justin doesn't have a large AI contact base yet, so v1 is about building the list and making genuine connections before any pitch.\n\n**Channel:** LinkedIn only (via Unipile from Justin's account)\n\n---"
    },
    {
      "title": "ICP",
      "level": 2,
      "body": "DreamBorn targets:\n- AI companies and startups\n- Thought leaders in AI\n- Large manufacturers and distributors with AI initiatives\n\nDreamBorn sells: AI workflow and orchestration engine for agents — consensus and trust infrastructure. The pitch is what an AI-native company looks like in practice. Audience: founders, operators, builders.\n\n---"
    },
    {
      "title": "Architecture",
      "level": 2,
      "body": "Traci runs on `roles.sales` HCS topic. Same runner pattern as all other agents.\n\n**Coordination layer split:**\n- **HCS** — workflow-level events: task dispatch, exec gate approvals, batch complete\n- **Supabase** — granular timing state: send queue, per-send status, activity logging\n\nIndividual LinkedIn sends are too frequent and cheap to put on HCS. The `traci_send_queue` table handles timing; HCS handles meaningful workflow transitions.\n\n---"
    },
    {
      "title": "`prospect_search`",
      "level": 3,
      "body": "Justin posts a brief (e.g. \"50 AI startup founders, Series A-B, US-based\"). Traci:\n1. Searches LinkedIn via Unipile using the brief as search criteria\n2. Deduplicates against existing `crm_contacts` (skip anyone already in CRM)\n3. Writes a `prospect_batches` row with all candidates + proposed connection message per candidate\n4. Posts to `roles.exec` — Justin reviews in Cockpit, approves/rejects per candidate\n5. Approved candidates written to `crm_contacts` with `outreach_status: prospect`"
    },
    {
      "title": "`send_connections`",
      "level": 3,
      "body": "Dispatched by Engine after Justin approves a batch. Traci:\n1. Reads approved candidates from `prospect_batches`\n2. Distributes sends across the day (8am–6pm, ~3/hour, randomised within each hour)\n3. Writes rows to `traci_send_queue` with `scheduled_at` timestamps\n4. Enforces 75 send/day cap at queue-write time — rejects overflow\n5. Posts `task.complete` immediately (queue is written, not sends completed)\n6. On each subsequent runner tick: checks `traci_send_queue` for rows where `scheduled_at <= now() AND status = 'queued'`, sends via Unipile, logs to `crm_activities`, updates `outreach_status: connection_sent`\n7. Posts daily summary to `roles.exec`: \"Traci: sent N connection requests today\""
    },
    {
      "title": "`check_connections`",
      "level": 3,
      "body": "Traci polls Unipile for accepted connection requests (contacts where `outreach_status: connection_sent`). For each accepted connection, updates `crm_contacts.outreach_status = 'connected'` and logs a `crm_activities` row. This is what arms `draft_nurture`. Runs on every runner tick — lightweight Unipile + Supabase check, no HCS task needed."
    },
    {
      "title": "`draft_nurture`",
      "level": 3,
      "body": "Traci scans `crm_contacts` where `outreach_status: connected` and no active pipeline deal. For each:\n1. Pulls their profile context (title, company, icp_tags)\n2. Drafts a personalised follow-up DM\n3. Batches all drafts into a `prospect_batches` row\n4. Posts to `roles.exec` for review\n5. Justin approves per-message → `send_message` task dispatched for approved items"
    },
    {
      "title": "`send_message`",
      "level": 3,
      "body": "Same queue pattern as `send_connections` but for DMs to already-connected contacts. Logs to `crm_activities`, updates `outreach_status: nurturing`.\n\n---"
    },
    {
      "title": "New fields on `crm_contacts`",
      "level": 3,
      "body": "| Field | Type | Notes |\n|---|---|---|\n| `outreach_status` | text | `prospect \\| connection_sent \\| connected \\| nurturing \\| meeting_booked \\| not_interested` |\n| `source` | text | `traci_search \\| manual \\| inbound` |\n| `icp_tags` | text[] | e.g. `['ai-startup', 'founder', 'series-a']` |\n\n`linkedin_url` already exists on Arlo's field list."
    },
    {
      "title": "New table: `prospect_batches`",
      "level": 3,
      "body": "```sql\nid              uuid primary key default gen_random_uuid()\nclient_id       text not null\ntask_id         text                  -- HCS task topic ID\nbrief           text                  -- Justin's search brief\ncandidates      jsonb                 -- [{name, title, company, linkedin_url, linkedin_urn, proposed_message, approved}]\nstatus          text                  -- pending_review | partially_approved | sent\ncreated_at      timestamptz not null default now()\n```"
    },
    {
      "title": "New table: `traci_send_queue`",
      "level": 3,
      "body": "```sql\nid              uuid primary key default gen_random_uuid()\nclient_id       text not null\ncontact_id      uuid references crm_contacts(id)\nsend_type       text not null         -- connection_request | dm\nmessage         text not null\nlinkedin_urn    text not null\nscheduled_at    timestamptz not null\nsent_at         timestamptz\nstatus          text not null default 'queued'  -- queued | sent | failed\nerror           text\ncreated_at      timestamptz not null default now()\n```\n\n**Activity logging:** Every send gets a `crm_activities` row — type `linkedin_connection` or `linkedin_dm`. Traci and Arlo share the same activity log table.\n\n---"
    },
    {
      "title": "Unipile Integration",
      "level": 2,
      "body": "Traci authenticates via Unipile using Justin's LinkedIn account credentials (stored as `UNIPILE_ACCOUNT_ID` + `UNIPILE_API_KEY` in VPS env).\n\n**Connection request:**\n```\nPOST /messaging/accounts/{account_id}/connections\n{ linkedin_member_urn, message }   -- message: 300 char max\n```\n\n**DM:**\n```\nPOST /messaging/accounts/{account_id}/messages\n{ conversation_id, text }\n```\n\n**Rate limiting:**\n- Max 75 sends/day enforced at queue-write time\n- `delay_between_sends_ms: 3000` (default) — configurable per client config\n- Sends distributed 8am–6pm with randomised within-hour timing\n\n---"
    },
    {
      "title": "Voice + Writing Rules",
      "level": 2,
      "body": "Traci writes from Justin's LinkedIn account. Messages must sound like him.\n\n- First person (\"I\", never \"we\")\n- Short: connection notes under 200 chars, DMs under 5 sentences\n- Lead with something specific to the prospect — their role, recent move, company milestone, or content they posted\n- No pitch in the connection request — just a genuine reason to connect\n- The ask comes in DM #2 or #3, never the first message\n- Never use \"I came across your profile\"\n- Traci's persona includes Justin's voice from BezelBrain captures\n\n**Connection note pattern:**\n> \"[Specific observation about them or their work] — would love to connect.\"\n\n**First DM (after acceptance):**\n> One paragraph on why DreamBorn is relevant to what they're building. No ask. End with a question.\n\n**Second DM (no reply after 7 days):**\n> One sentence. Different angle.\n\n---"
    },
    {
      "title": "HCS Topic Structure",
      "level": 2,
      "body": "One new role topic required:\n\n| Topic | Purpose |\n|---|---|\n| `roles.sales` | Traci claims prospect_search, send_connections, draft_nurture, send_message tasks |\n\n---"
    },
    {
      "title": "Exec Gate Pattern",
      "level": 2,
      "body": "Every outbound action requires Justin's approval before Traci sends:\n\n1. `prospect_search` complete → full candidate batch posted to `roles.exec`\n2. Justin approves/rejects per candidate in Cockpit\n3. Engine dispatches `send_connections` for approved batch\n4. `draft_nurture` complete → DM drafts posted to `roles.exec`\n5. Justin approves/rejects per message\n6. Engine dispatches `send_message` for approved items\n\nJustin reviews everything at the start. As trust builds, approval cadence can relax.\n\n---"
    },
    {
      "title": "Agent Definition",
      "level": 2,
      "body": "```json\n{\n  \"slug\": \"traci\",\n  \"name\": \"Traci\",\n  \"department\": \"sales\",\n  \"model\": \"claude-sonnet-4-6\",\n  \"schedule_seconds\": 120,\n  \"roles\": [\"sales\"],\n  \"skills\": [\"hcs-posting\"],\n  \"plugins\": [\"hedera-tools\"],\n  \"persona\": \"See agents/traci/persona.md\"\n}\n```\n\n---"
    },
    {
      "title": "Scope Boundary",
      "level": 2,
      "body": "**V1 includes:**\n- `prospect_search`, `send_connections`, `draft_nurture`, `send_message` task types\n- `traci_send_queue` + `prospect_batches` Supabase tables\n- New fields on `crm_contacts`\n- Unipile integration (connection requests + DMs)\n- Daily 75-send cap + distributed scheduling\n- Exec gate for all sends\n- Traci agent definition + persona\n\n**V1 excludes (future):**\n- Autonomous ICP-driven prospecting (no brief needed — Traci searches on schedule)\n- Rex researcher integration (Traci pulls research briefs from Rex)\n- Reply handling (detecting and responding to LinkedIn replies)\n- Brooke (Sales) handoff automation\n- Cockpit sales tab (separate project)"
    }
  ],
  "html_path": "artifacts/2026-04-27-traci-sdr-design-20fb4b9b99.html",
  "json_path": "artifacts/2026-04-27-traci-sdr-design-20fb4b9b99.json"
}