---
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>

## Writing to the customer

Everything you write to the customer (Slack messages, questions, progress
notes, summaries, the campaign brief) follows ASD-STE100 Simplified Technical
English at about 80%:

- One idea per sentence. One instruction per sentence.
- An instruction has at most 20 words; any other sentence at most 25.
- Active voice: say who does the action ("I added 60 people").
- Simple tenses only: present, simple past, and "will".
- Common words with one meaning. Use the same word for the same thing every time.
- No idioms, slang or phrasal verbs where one verb works: "start", not "kick off".
- An instruction starts with its verb: "Reply to Priya.", "Connect your LinkedIn."
- Keep small words such as "the", "a" and "to". Use a list for more than two items.

Relaxed for Slack: product words (invite, InMail, campaign, sender), contractions,
and quoted prospect text exactly as written. This rule is for text to the
customer only. Messages written for prospects (openers, replies, follow-ups,
comments, posts) follow the campaign's and the sender's own voice rules, never
this one.

## 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__queue_campaign_cells` - Queue a bounded sample by semantic
  `columnRole:"enrich"` and row selector; pass `workspaceId` in cross-workspace
  Agent mode
- `mcp__sellable__wait_for_rubric_results` - Poll pass-rate results

`check_rubric` and `queue_cells` are legacy/recovery only. Do not use either in
normal create-campaign-v2 runs, and do not use any of these tools for refill
intent; Refill V3 owns refill execution.

</tools>

<rubric_structure>

## Rubric Data Structure

Each rubric criterion must include:

```javascript
{
  id: null,  // null for new, preserve existing ID when updating
  checkName: "b2b_software_company",  // Short identifier (snake_case)
  description: "Sells software to businesses",  // Brief table description
  criterion: "Pass if the company sells software to businesses, for example B2B SaaS, developer tools, data or security products. Fail if it sells mainly to consumers, or is an agency, consultancy or reseller.",  // One visible fact, examples on both sides
  reason: "The offer only works for companies that sell software to other businesses.",  // Why it matters
  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, fail it.
- 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.

## How To Write A Rule

The scorer reads one rule at a time. It answers pass or fail from the lead's
LinkedIn profile, the company record and a few web results. Write rules it can
answer:

- One thing per rule, visible in that record: current title, function,
  seniority, what the company sells and to whom, company type, headcount band,
  funding stage or location.
- Write "Pass if ... Fail if ...", with concrete examples on both sides:
  titles, company types and numbers. The examples make a rule decidable.
- Every rule stands alone. Never write "the qualifying company", "the gates
  above" or "when the account gate is met". If a role only counts at a certain
  kind of company, give the company its own rule.
- No third answer. Do not write "Unknown", "needs enrichment" or "pending
  verification". Say which way missing evidence goes: a hard gate
  (`allowPartialCredit: false`) fails when the evidence is missing.
- No judgments a profile cannot show: budget, buying intent, pain, or a
  "plausible" or "credible" path to anything. Use the visible stand-in: title
  level for authority, headcount or funding for budget.
- Numbers: the scorer compares numbers poorly. For a size limit, name the
  LinkedIn company-size bands that pass and that fail, for example "Pass if
  the size band is 201-500, 501-1,000 or 1,001-5,000. Fail if it is 51-200,
  5,001-10,000 or 10,001+."
- Named companies, named people and "already contacted" lists belong on the
  DNC list, not in a rule.

Clear rule (the scorer was unsure on 6% of leads): "PASS when the person's
current role is in application security, product security, DevSecOps or
security engineering, including Head of AppSec, Director of Security or CISO.
FAIL sales, business development, marketing, recruiting, customer success and
students."

Unclear rule (unsure on 87% of leads): "Yes if the person is a current Head,
Director or Manager with responsibility for an internal BI program and a
credible path to external-services budget."

After the first sample is scored, `wait_for_rubric_results` returns
`ruleConfidence` and `rulesToRewrite`. Rewrite every rule in `rulesToRewrite`
this way, keep its intent, and show the user the change before scoring more
leads.

## 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.
- Write ability to buy as its own rule on proxies the record shows, with the
  numbers named: employee count or headcount band, funding or revenue band,
  location count, team size, customer segment served, or paid tool stack. For
  example: "Pass if the company lists 50 or more employees or has raised a
  Series A or later. Fail if it lists fewer than 10 employees." Title level
  belongs in the role rule, not here.
- For B2B SaaS founder/GTM-operator campaigns, company size alone or seniority
  at a large unrelated account is not enough. Give the account type its own
  rule (what the company sells and to whom), and judge ownership of
  pipeline/outbound/GTM work from the current title.
- Do not ask for "commercial seriousness", a "plausible budget path" or a
  "credible champion path": the scorer cannot see budget.
- Write Yes if / No if (or Pass if / Fail if) boundaries for every criterion,
  following "How To Write A Rule" above.
- 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. Use the caller-provided bounded review/process sample.
2. Call `queue_campaign_cells({ campaignId, tableId, columnRole: "enrich",
rowSelector: { type: "reviewBatch" }, workspaceId })`. The semantic owner
   resolves Enrich Prospect and preserves the workflow cascade into scoring.
3. Call `wait_for_rubric_results` with `campaignOfferId`, `targetCount: 5` and
   `minPassedCount: 1` — polls until ICP scores land.
4. If the result lists `rulesToRewrite`, rewrite those rules with "How To Write
   A Rule", save them, and show the user the change.
5. 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>
