# Candidate review workflow

> Review extracted terms against five tests before they earn a place in the ontology.

**Category:** Terminology | **Tab:** features

---

# Candidate review workflow

A **candidate** is a word or phrase your pipeline extracted (from UI strings, docs, support tickets) that *might* deserve a place in the ontology. Candidates are **not** part of the ontology: they are never returned by the Read API, the MCP server, or GitHub Sync.

Black Ice holds no model. Extraction runs in **your** pipeline; Black Ice stores the candidates and records the human decision.

```text
Your extraction --> Candidates queue --> Approve --> new concept, source term = Pending
                                     +-> Merge   --> added as a synonym (allowed variant)
                                     +-> Reject  --> rejection list (suppressed next time)
```

## The five tests

Open a candidate from **Candidates** in the sidebar and mark each test Pass, Unsure or Fail. The answers are saved with the decision.

1. **Unique** — is it already there under another name? This is the primary gate. If you can't articulate a real difference in meaning from an existing concept, merge it as a synonym. The dialog lists similar existing concepts.
2. **Distinct** — does it have a clean boundary with its neighbours?
3. **Necessary** — is it costly to miss? Does a translator guess, a support ticket get filed, legal risk appear, a UI label stop matching docs?
4. **Stable** — is the meaning settled? If not, wait (Needs definition work) or approve as **provisional**.
5. **Recurring** — will it come up again across products, content types or languages?

> A candidate belongs when it is unique, distinct, necessary, stable and recurring. Miss unique or necessary and the entry is almost always noise. The other three can usually be fixed with better definition work.

## Decisions

- **Approve** — creates the concept with its source term as **Pending**, ready for target-term work. Provenance is kept on the concept.
- **Merge as synonym** — adds the word to an existing concept's allowed variants.
- **Needs definition work** — keeps it out of the main queue with a note.
- **Reject** — requires a reason (not unique, not distinct, not necessary, not stable, not recurring, other).

## The rejection list

Rejected words are remembered per workspace and locale. When your pipeline sends the same word again it is **suppressed** — only its counters update. It returns to the queue as **Resurfaced** only when its frequency reaches a multiple (default **3×**) of its frequency at rejection. Change the multiple on the Rejected tab.

## Confidence scores

Candidates are ordered by confidence, then frequency. Confidence orders the queue; it never replaces the tests.

## Acceptance rate

The Candidates page shows acceptance per source for the last 90 days, plus the most common failure. **If fewer than one in five candidates is accepted, your extraction prompt is too permissive** — it is collecting vocabulary instead of concepts.

## Start narrow

Run extraction on one product's UI strings first, tune the prompt against what the terminologist actually accepts, then add sources. Use a distinct `source` label per source so acceptance is tracked separately.

## Sending candidates

Use a **read-and-write** API key:

```bash
curl -X POST https://oosdjmpzvcljxwaqxcko.supabase.co/functions/v1/mutate-ontology \
  -H "x-api-key: bice_..." -H "Content-Type: application/json" \
  -d '{
    "candidates": [
      {
        "term": "Noa Notes",
        "locale_code": "en-us",
        "source": "Product A UI strings",
        "frequency": 14,
        "confidence": 0.82,
        "class_name": "Feature",
        "definition": "AI note-taking assistant for consultations.",
        "provenance": [{ "file": "app/strings.json", "key": "notes.title", "url": "https://github.com/acme/app/blob/main/app/strings.json", "snippet": "Open Noa Notes" }]
      }
    ]
  }'
```

Up to 500 candidates per request. The response reports `created`, `updated`, `suppressed` (on the rejection list) and `resurfaced`. Only `term` and `locale_code` are required.

You can also import a CSV from the Candidates page with columns `term, locale_code, source, frequency, confidence, provenance_url, definition`.


## Adding a candidate manually

Spotted a term yourself — in a support ticket, a UI string, a review? Click **Add candidate** on the Candidates page. Enter the term and market (defaults to your source market), and optionally a suggested class, a proposed definition, and where you found it. Manual entries are labelled **manual**, so they appear as their own source in acceptance stats. The usual rules apply: words on the rejection list stay hidden, and a word already in the queue has its times-seen count increased instead of being duplicated.


## Doing the definition work

Mark a candidate **Needs definition work** when the idea is right but the definition isn't ready. Later, open it from the Needs definition work tab: the proposed definition and suggested class are editable. Rewrite the definition, re-run the Distinct and Stable tests, then click **Save draft** to keep working later — or go straight to **Approve as new concept**, which creates the concept with the definition as you wrote it.


## Suggest candidates from your AI assistant

If you connect Claude, Cursor or another assistant through the [MCP connector](/docs/integrations/connect-claude-mcp), it can call the `suggest_candidates` tool to send words it spots — for example while reviewing UI strings or support tickets — straight into this queue.

**It is off by default.** A workspace owner or admin turns it on in **Integrations → AI connection → Allow AI assistants to suggest candidates**. While it is off, the connection is purely read-only and assistants don't even see the tool.

Black Ice runs no AI of its own. Your assistant (Claude, Cursor, or another MCP client) does the spotting; Black Ice only stores its suggestions in the review queue, and people decide.

- **Who can use it:** a read-and-write API key, or a Claude sign-in by an editor, admin or owner. Read-only keys and viewers get a clear refusal.
- **What it sends:** term, market, optional definition, suggested class, a link or note on where it was found, and an optional confidence score. Up to 50 per call.
- **Same rules:** rejected words stay hidden unless their frequency grows by your resurface multiplier; repeats raise "times seen" instead of creating duplicates.
- **Labelled separately:** entries arrive with the source `ai_assistant` (plus the client, when known), so they get their own row in *Acceptance by source*. If fewer than one in five is accepted, tighten the prompt you give the assistant.
- **Nothing becomes binding:** the tool cannot approve, merge, reject or edit anything. Only approved terms are binding.

Example prompt:

```
Read these UI strings and suggest to Black Ice any product-specific terms that
are not already in the ontology. Check with list_concepts first, then call
suggest_candidates with a note saying which file each term came from.
```


## Example: Claude Code suggesting candidates

![Claude Code terminal showing 8 candidates sent to the Black Ice review queue with class, confidence and notes](/__l5e/assets-v1/be3dcd62-11f0-4b1a-a0e8-ffcd77ae2419/claude-code-suggest-candidates.png)

*Claude Code read a product post and suggested 8 words. They land in the Queue as unconfirmed candidates until a terminologist reviews them.*

![Black Ice Candidates queue showing the 8 words suggested by the AI assistant, each queued with its locale, source and confidence](/__l5e/assets-v1/02f06d71-74b6-484a-9f07-487d1bbc8a7a/candidates-queue-ai-suggestions.png)

1. An owner or admin turns on **Allow AI assistants to suggest candidates** under Integrations → AI connection.

   ![The AI connection page with "Allow AI assistants to suggest candidates" switched on, showing 8 tools including suggest_candidates](/__l5e/assets-v1/e7a1fb5c-fd5e-443a-9a7b-2a623ddc1b8e/ai-suggestions-switch.png)

2. Connect Claude Code with a read-and-write API key, or sign in as an editor, admin or owner.

   ![The API Access page where you create a key and choose its access level, with a "Read & write" key listed under Active Keys](/__l5e/assets-v1/d8c0f04f-7cca-40af-b387-abb66abe1e42/connect-claude-code.png)

3. Paste a text and ask: *"Suggest candidates from this text using suggest_candidates."*

The assistant skipped terms that were already approved and flagged wording that differs from approved terms. Those are helpful hints, but only the Queue decides what becomes terminology.
