> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Scorecard

> Weighted rules with sigmoid normalization. Operator-defined, fully transparent, no training data needed. The model every fresh deployment should start with.

`modelType: "scorecard"` — a points-based scoring model where each rule contributes a configurable number of points when its condition matches. Points accumulate from a base score, then normalize to a probability via sigmoid (or linear). The math is intentionally simple so operators, auditors, and regulators can read it line-by-line.

## When to use

* **Day-1 launch** — no interaction history yet. Hand-craft rules from domain expertise.
* **Auditability is non-negotiable** — financial services, healthcare, anything regulated. Every rule fired is recorded in the score explanation.
* **Cold-start mitigation while you build a real model** — scorecard runs as the active model while you accumulate outcomes for a future `bayesian` or `gradient_boosted` candidate.

**Skip it when** you have ≥ 1k labeled outcomes and meaningful feature interactions — a learned model (logistic, GBT) will outperform on accuracy, and you can fall back to scorecard for the cold-start segment via `Score.overrides[]`.

## The math

```
rawScore = baseScore + Σ (rule.points if rule.matches(customer) else 0)

# Sigmoid normalization (default):
midpoint = (maxScore + minScore) / 2
range    = maxScore - minScore
x        = ((rawScore - midpoint) / range) * 6   # 6 maps ±range to ±3 σ
score    = 1 / (1 + e^(-x))

# Linear normalization:
score    = (rawScore - minScore) / (maxScore - minScore)   # clamped to [0, 1]
```

Each rule applies one of 10 operators (`eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `not_in`, `contains`, `starts_with`) to one attribute on the candidate or its customer enrichment.

## Fixture config

```json theme={null}
{
  "name": "Prime credit scorecard",
  "baseScore": 50,
  "minScore": 0,
  "maxScore": 100,
  "normalization": "sigmoid",
  "rules": [
    { "id": "r1", "field": "credit_score", "operator": "gte", "value": 740, "points": 20, "description": "Prime credit" },
    { "id": "r2", "field": "credit_score", "operator": "gte", "value": 800, "points": 10, "description": "Super-prime bonus" },
    { "id": "r3", "field": "income",       "operator": "gte", "value": 75000, "points": 10, "description": "High income" },
    { "id": "r4", "field": "segment",      "operator": "eq",  "value": "Gold", "points": 8, "description": "Loyalty boost" },
    { "id": "r5", "field": "age",          "operator": "gte", "value": 25,    "points": 2, "description": "Established demo" }
  ]
}
```

This fixture is exercised in the algorithm-coverage proof: `platform/scripts/proof-algorithms-e2e.mts` shows it scores `0.917` for a customer with `{credit_score: 760, income: 95000, age: 38, segment: "Gold"}` — 4 of 5 rules fire (the super-prime bonus at 800+ doesn't, credit\_score 760 \< 800).

## Training

There's no training step. Operators add and tune rules through the **Algorithms → Models → Scorecard** UI. Each rule's `points` value is the only knob.

If you want data-driven rule weights, train a `logistic_regression` model on the same predictors and read off the weights as a starting point for scorecard `points`.

## Score interpretation

* `score` ∈ `[0, 1]` — the engine consumes this as the **propensity component** (P) in PRIE, or as the raw score in `propensity` strategy.
* `rawScore` ∈ `[minScore, maxScore]` — the pre-normalization point total. Operators reading the trace can tell exactly how many points each rule contributed.
* `explanations[]` — one entry per rule, with `{matched, points, field, expected, actual, description}`. This is the audit trail.

## Pitfalls

* **All-or-nothing rule sets** — if all your rules are `points: 10` and only one fires per candidate, every score is 50 or 60. Vary the point values so high-value rules can dominate.
* **Forgetting to set `minScore` / `maxScore`** — defaults are `[0, 100]`. If your rules total > 100 in worst-case, the sigmoid will saturate at 1.0 for every prime customer.
* **No rules at all** — every candidate scores `baseScore` normalized (= 0.5 by default). Documented in T20 Test C of the proof bundle, where `scorecard-v2` was created with zero rules and Platinum's adaptation `positiveRate=0` triggered the starvation bug (now fixed via the [propensity floor](/decisioning/scoring-strategies#why-the-floor-exists--the-starvation-failure-mode)).
* **Categorical fields with high cardinality** — `eq` rules need one rule per value. Use `in` with an array instead.

## Lifecycle & cadence

Scorecard is **offline-retrain only**. The platform won't update rule weights or AUC unless an operator explicitly triggers it. Out of the box (`autoLearn: false, learnMode: "none"`) a scorecard model is frozen at whatever values were in `predictors[]` at creation time and `metricsHistory` stays empty.

To make it learn: `PUT /algorithm-models/{id} { "autoLearn": true, "learnMode": "scheduled", "learnSchedule": "24h" }`. Default cadence on the cron is nightly (24h). For weekly cadence on stable workloads, set `"7d"`. The retrain pass recomputes rule importance from accumulated `interaction_history` and writes a new entry to `metricsHistory[]`. See [Learning cadence](/ai-ml/learning-cadence) and [Model lifecycle](/ai-ml/model-lifecycle) for the full 4-step path from creation to live champion.

## Cross-reference

* [Algorithm Selection Guide](/decisioning/algorithm-selection-guide) for when scorecard is the right pick.
* [Scoring Strategies](/decisioning/scoring-strategies) for how the score is consumed by the Score node.
