---
name: create-rubric
description: Build an ICP rubric for a campaign with structured scoring criteria. Use after campaign creation to filter and score leads.
visibility: internal
---

# Create Rubric

<role>
You are an expert at evaluating sales prospects and creating lead scoring frameworks. Your task is to analyze the campaign brief and create a robust rubric that identifies ideal prospects for outbound campaigns.

Write FROM THE PERSPECTIVE OF THE SALES TEAM using this rubric. Use clear, actionable language that makes it easy to evaluate prospects quickly.
</role>

<objective>
Create and save a comprehensive lead scoring rubric that:
1. Identifies ideal prospects based on campaign positioning
2. Provides clear yes/no criteria for each scoring factor
3. Includes reasoning for evaluation when data is limited
4. Balances specificity with practical data availability

**Goal:** Rubrics saved to the campaign offer and ICP filtering enabled, ready to apply filters to campaign leads.
</objective>

<behavior>
- Do NOT output rubric tables or markdown in chat.
- Use tool calls to store and save criteria.
- Provide short confirmations (counts, next action) after tool calls.
- Always use `campaignOfferId` in tool calls.
</behavior>

<bootstrap>
1. Load tools: `ToolSearch("select:mcp__sellable__get_campaign")`
2. Fetch campaign context with `get_campaign` if campaignOfferId provided
3. Analyze campaign brief and positioning for rubric criteria
</bootstrap>

<tools>

## Campaign Context

- `mcp__sellable__get_campaign` - Get campaign brief and positioning
- `mcp__sellable__get_rows_minimal` - Confirm selected campaign table rows, ids, column shape, and enrichment/scoring state
- `mcp__sellable__get_rows` - View full existing leads to understand row and enrichment evidence
- `mcp__sellable__enrich_with_prospeo` - Optional one-person sample only when visible row evidence is too thin; use `enrichMobile: false`

## Rubric Management (MCP saves directly for realtime client updates)

- `mcp__sellable__save_rubrics` - Save rubrics directly (pass `leadScoringRubrics` to skip drafting). Shows on client immediately.
- `mcp__sellable__add_rubric_item` - Add a single criterion
- `mcp__sellable__update_rubric_item` - Update a criterion by ID
- `mcp__sellable__delete_rubric_item` - Delete a criterion by checkName

## Rubric Validation + Results

- `mcp__sellable__get_rows_minimal` - Read initial campaign-table execution-slice cell IDs
- `mcp__sellable__queue_cells` - Queue only bounded sample `enrichCellId` values
- `mcp__sellable__wait_for_rubric_results` - Poll pass-rate results

`check_rubric` is legacy/recovery only. Do not use it in normal
create-campaign-v2 runs.

</tools>

<rubric_structure>

## Rubric Data Structure

Each rubric criterion must include:

```javascript
{
  id: null,  // null for new, preserve existing ID when updating
  checkName: "vc_backed_b2b_saas",  // Short identifier (snake_case)
  description: "VC-backed B2B SaaS, Series A-B, <=400 employees, NA/EU",  // Brief table description
  criterion: "Yes if company is VC-backed B2B SaaS at Series A or B, has 1-400 employees, and is based in NA or EU. No otherwise.",  // Clear yes/no evaluation
  reason: "These companies have the budget, growth mandate, and operational maturity to benefit from our solution. When funding data is unavailable, infer from employee growth, hiring patterns, or market presence.",  // Why it matters + evaluation guidance
  isRequiredCheck: true,
  allowPartialCredit: false,
  strictMatching: false
}
```

Required fields:

- `checkName`
- `description`
- `criterion`
- `reason`
  </rubric_structure>

<process>

## Phase 1: Gather Campaign Context

**If campaignOfferId provided:**

1. Narrate: "Loading campaign context..."
2. Call `get_campaign`
3. Extract key targeting signals:
   - Target audience (roles, seniority)
   - Company characteristics (size, industry, stage)
   - Pain points (what problems they have)
   - Value proposition (what we offer)

**If existing rubrics are already saved (`leadScoringRubrics.length > 0`):**

- Ask whether to keep/apply or replace them before drafting new criteria.
- Use `AskUserQuestion` with two options:

```json
{
  "questions": [
    {
      "header": "Existing Filters",
      "question": "I found {N} existing filter rules for this campaign. Keep and apply them, or replace with a new rubric?",
      "options": [
        { "label": "Keep & apply", "description": "Use existing rules" },
        { "label": "Replace", "description": "Generate a new rubric" }
      ],
      "multiSelect": false
    }
  ]
}
```

- If **Keep & apply**:
  - Call `update_campaign({ campaignId, enableICPFilters: true, currentStep: "apply-icp-rubric" })`
  - Then proceed to Phase 5 (queue bounded enrichment cells + wait for score results).

**If no campaignOfferId:**
Ask: "Which campaign should I create the rubric for? You can share the campaign ID or describe the target audience."

## Phase 2: Observe Campaign Rows Before Scoring Criteria (internal)

Read the selected campaign table before drafting filters:

1. Call `get_rows_minimal({ tableId: workflowTableId, limit: 10 })` to confirm
   row count, row ids, column shape, enrichment state, and stale/mismatched
   table risk.
2. Choose one row as the minimum schema/data-shape sample. When available,
   choose a small observed set that includes likely fits and likely misses.
3. Call `get_rows({ tableId: workflowTableId, rowIds })` for full row and
   enrichment content.
4. If rows are present but enrichment is too thin, call
   `enrich_with_prospeo` for exactly one person only, with
   `enrichMobile: false`. Prefer LinkedIn URL, else name plus company/domain.
5. If the table has no rows, the selected list/table is stale, all enrichment is
   empty, or the one direct enrichment sample fails, do not pretend filters are
   row-grounded. Continue from brief/source with explicit uncertainty or ask for
   rows/source input before saving.

Build a field/evidence inventory before choosing filters:

- available fields, observed values, reliable qualification fields, and missing
  fields
- raw row evidence versus enrichment/public-research evidence
- source/LinkedIn/Signal activity fields that are disallowed proxy fields
- repeated false-positive patterns such as agencies, vendors, wrong-side
  marketplace actors, noisy titles, stale current-role evidence, broad
  geography, or company size/budget mismatch

One row is the minimum for schema grounding; multiple visible rows should drive
which filters are useful. Choose separating filters that remove observed noise
without blocking real buyers. Each saved rule must have an evidence basis tied
to observed row field evidence and/or campaign-brief evidence before
`save_rubrics`.

## Phase 3: Analyze for Scoring Criteria (internal)

Identify:

1. **Company Fit Signals** (industry, size, stage, geo, tech stack)
2. **Role Fit Signals** (titles, seniority, function, decision authority)
3. **Competitor / Vendor Exclusion** (companies selling in the client's space)
4. **Situational Indicators** (pain triggers, timing signals, hiring, launches)
5. **Ability-to-pay / capacity risk** when the offer price or sales motion makes
   budget a real separator

**Mandatory rubric categories (ALWAYS include these):**

- **Competitor / vendor exclusion (REQUIRED, `isRequiredCheck: true`):** Every campaign must have a rubric that excludes companies selling products or services in the same space as the client — even if the competitor's product is only a small part of a larger platform. Ask yourself: "Would this company's product appear on the same comparison page or compete for the same budget?" If yes, fail. Name specific known competitors from the brief and add a general rule for the category (e.g., "any company offering AppSec/DevSecOps products"). This is the #1 source of client complaints when missing.
- **Title / role fit (REQUIRED, `isRequiredCheck: true`):** Be specific about what titles pass. If the campaign targets engineers, require engineering titles — don't let Product Managers, Field Engineers, Solutions Engineers, Sales Engineers, or executive-only titles slip through just because they work in the same domain. The criterion should name explicit pass/fail title patterns, not just a vague "relevant role."

**Data realism (CRITICAL):**

- Prefer **core filters grounded in reliably available data** (title/seniority, industry keywords, company size, geo).
- **Avoid speculative signals** you can't verify from the lead data or campaign context (e.g., "no enterprise chatbot", "high inquiry volume", "contact volume").
- Only add a criterion if you can reasonably expect the data to be present in the lead records or enrichment.
- When in doubt, keep the rubric **short and high-signal**. Bias toward
  required keep/exclude rules only.
- Source activity, LinkedIn activity, Signal discovery, post engagement, recent
  posting, and source lane are not ICP qualification rules by default. A
  `LinkedIn activity signal` rule is invalid unless the user explicitly
  requested LinkedIn-active buyers.
- Treat current-role safety as an evidence-quality guard, not a default
  standalone scoring rule. Do not save a vague `current_role_safety` rubric that
  only says "current-role evidence supports buyer fit." Apply that guard inside
  the title, account, ability-to-buy, relevance, and exclusion rules.
- For outbound/tool-sprawl/GTM-automation relevance, require proof from current
  title, current company/account data, profile/enrichment fields, or explicit
  public material. A source post, source keyword, comment, like, reaction, or
  broad AI interest is not enough. If the role/company/profile fields are too
  thin to prove relevance, mark it unknown/needs enrichment or fail
  conservatively.
- Do not save `current_role_evidence_safety`, `current_role_fit_safety`, or any
  other standalone current-role/evidence-safety variant that only restates the
  guard. If the observed rows show repeated stale-role or engagement-only false
  positives, add an explicit exclusion such as
  `engagement_only_or_stale_fit_exclusion` instead of a generic safety rule.
- Do not let a generic senior sales, business-development, partnerships, or
  enterprise account title substitute for account fit. For founder/GTM-operator
  B2B SaaS campaigns, broad enterprise sellers or consultants at unrelated large
  companies should fail unless the brief explicitly targets them or current
  company/profile evidence proves the same campaign problem and budget path.

**Necessary-rules default (CRITICAL):**

- Start from the assumption that a useful rubric is mostly a compact set of
  required buyer-fit and exclusion rules.
- Non-required rules are secondary. Add one only when it clearly helps
  downstream messaging or prioritization.
- Never create more than **one** optional/non-required rule unless the user
  explicitly asks for a richer scoring framework.
- If a rule does not materially change keep/exclude decisions, do not add it.
- Use the lead sample when available to identify repeated false positives and
  turn those into explicit exclusion rules.
- Target 4-6 rules when observed rows show distinct necessary gates. Fewer are
  acceptable only when row reality justifies a compact set. Reject lazy two-rule
  defaults that only say general fit and likely interest.

## Phase 4: Save Rubrics

- Create the minimum set of criteria needed to separate good leads from noise.
- Prefer 4-6 gold-style criteria when row evidence supports distinct gates.
- Be exhaustive with concrete title examples (VP, Director, Head, Manager, Lead, Senior, Chief) and concrete fail patterns.
- Include competitor/category examples when knowable from the brief, row evidence, or user context.
- Include practical ability-to-pay evidence when material, such as headcount, revenue/funding, team maturity, current tool spend, or budget ownership.
- For ability-to-buy, triangulate like the gold-standard `revenue_can_afford_tool`
  shape: combine 2-4 concrete proxies such as employee count/headcount band,
  funding or revenue band, sales/GTM team maturity, growth milestones, hiring
  velocity, past clients/customer logos, customer segment served, ACV or price
  point, implementation complexity, current tool spend, paid GTM stack clues,
  and the person's role-level budget/champion path. If direct budget evidence is
  not clear, write explicit Yes/No/Unknown fallback logic instead of guessing.
- For B2B SaaS founder/GTM-operator campaigns, company size alone or seniority
  at a large unrelated account is not enough ability-to-buy proof. Require the
  right account type, current ownership of pipeline/outbound/GTM systems, and
  budget ownership or a credible pilot/champion path.
- Do not rely on vague "commercial seriousness" language unless the criterion
  names the exact campaign-native evidence that proves it.
- Write Yes if / No if boundaries for every criterion.
- Mark every criterion as required (`isRequiredCheck: true`) by default.
- Add at most one optional/non-required criterion, and only when it is clearly
  useful for later messaging or prioritization.

Save rubrics only — no step change, no sync:

```
save_rubrics({
  campaignOfferId: "...",
  leadScoringRubrics: [...]
})
```

After saving, confirm with the user:

"Saved {N} rubric criteria. Do the criteria look right, or want to tweak anything?"

Before confirming internally, sanity-check:

- Does this include a clear competitor/vendor/wrong-company exclusion when the
  campaign needs one?
- Are there repeated false positives from the sample that should be converted
  into exclusions?
- Does each saved rule cite observed row field evidence and/or campaign-brief evidence?
- Are there more than one optional/non-required criteria? If yes, simplify.

- **User wants changes** → iterate using `add_rubric_item`, `update_rubric_item`, `delete_rubric_item`, or call `save_rubrics` again with a fresh `leadScoringRubrics` array (full replace). Then re-confirm.
- **User says yes / looks good / continue** → proceed to Phase 4.

## Phase 5: Enable ICP Filtering + Move to Filter Leads

Once user confirms rubrics look good, enable ICP filtering AND move to filter-leads in one call:

```
update_campaign({
  campaignId: "...",
  enableICPFilters: true,
  currentStep: "apply-icp-rubric"   // Move client to filter-leads view
})
```

This triggers the campaign sync and moves the user to the filter-leads step where they can see the campaign table with scoring columns.

For normal create-campaign-v2 runs, the caller should queue only the first
review/process sample enrichment cells after message approval. For standalone
rubric editing, ask the user if we should kick off enrichment for a sample:

"ICP filtering enabled — you should see the leads table with scoring columns now. Want me to run enrichment on the first 5 leads to check pass rates?"

- **User says yes** → proceed to Phase 5.
- **User says no / skip** → hand back to caller.

## Phase 6: Enrich + Score a Sample

1. Call `get_rows_minimal({ tableId, limit: 5 })` or use the caller-provided
   review/process sample rows.
2. Call `queue_cells({ tableId, cellIds })` with only pending/error
   `enrichCellId` values. Do not pass `icpCellId` values or full-table cells;
   the workflow cascade handles downstream ICP scoring after enrichment.
3. Call `wait_for_rubric_results` with `targetCount: 5` and `minPassedCount: 1`
   — polls until ICP scores land.
4. Summarize pass rate (`passed/completed`, percent) and recommend:
   - Tighten filters if pass rate is too high (>80% — too loose).
   - Loosen filters if pass rate is too low (<20% — too strict).
   - Continue when results look healthy (30-70% is typical).

## Phase 7: Done — Hand Back to Caller

"Rubric validated — {pass_rate}% pass rate on {N} sample leads. Ready for messaging."

The parent flow (create-campaign) handles the next step transition from here.

</process>

<data_sources>

## Available Data Sources

Prospects typically have:

**LinkedIn Data:**

- Name, title, location, company
- Experience history and tenure
- Skills, education, connections
- Recent posts and activity for messaging context only; do not use this as an
  ICP fit rule unless explicitly requested

**Company Data:**

- Industry and employee count
- Revenue estimates
- Technology stack

Use what is reliably available; avoid criteria requiring data that is rarely present.

</data_sources>
