# How Bonus Points and Deductions Are Calculated in Resume Evaluations

> Learn how bonus points and deductions calculate resume evaluation scores. Understand the scoring system with a cap of 120 points and maximize your hiring agent effectiveness.

- Repository: [HackerRank/hiring-agent](https://github.com/interviewstreet/hiring-agent)
- Tags: internals
- Published: 2026-07-14

---

**Bonus points and deductions are applied to the base category scores after initial evaluation, with bonuses adding up to 20 points and deductions subtracting from the total, followed by a hard cap at 120 points.**

The `interviewstreet/hiring-agent` repository automates résumé screening using an AI-driven scoring system. After evaluating four core competency categories, the system applies **bonus points and deductions** to adjust the final score based on exceptional merits or identified shortcomings. Understanding this calculation logic helps engineers audit evaluation fairness and debug scoring discrepancies.

## Bonus Points Implementation

### Data Model Definition

The `BonusPoints` class in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) (lines 31-34) defines the schema for extra credit:

```python
class BonusPoints(BaseModel):
    total: int  # Range: 0-20

    breakdown: str  # Explanation of bonus criteria met

```

This model enforces a maximum of 20 bonus points and requires a human-readable `breakdown` string that explains which exceptional criteria the candidate met.

### Addition Logic in score.py

When the AI evaluator returns a `bonus_points` field, [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) adds the `total` to the running aggregate. The implementation at lines 58-60 checks for attribute presence before arithmetic:

```python
if hasattr(evaluation, "bonus_points") and evaluation.bonus_points:
    total_score += evaluation.bonus_points.total

```

The `breakdown` string is displayed to reviewers alongside the calculation for full transparency (lines 25-30).

## Deductions Calculation

### Deduction Schema

The `Deductions` model in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) (lines 36-41) captures penalties using a positive integer `total` and详细 `reasons` string:

```python
class Deductions(BaseModel):
    total: int  # Positive number to subtract

    reasons: str  # Detailed explanation of penalties

```

Unlike bonus points, deductions have no explicit upper bound in the model definition, allowing the evaluator to assign penalties proportional to the severity of identified shortcomings.

### Subtraction Logic

In [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 61-64), the system subtracts the deduction `total` from the accumulated score:

```python
if hasattr(evaluation, "deductions") and evaluation.deductions:
    total_score -= evaluation.deductions.total

```

## Score Capping and Boundary Enforcement

After applying bonuses and deductions, [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) enforces a hard ceiling to prevent score overflow. The logic at lines 65-69 caps the final score at the category maximum plus 20 bonus points:

```python
max_possible = sum(cat["max"] for cat in evaluation.scores.model_dump().values()) + 20
total_score = min(total_score, max_possible)  # Caps at 120 (100 + 20)

```

This ensures the theoretical maximum remains 120 points: 100 from base categories plus 20 bonus capacity. Deductions can lower the score significantly, though in practice scores remain positive because base category minimums typically offset penalties.

## Complete Evaluation Workflow

The following Python snippet demonstrates the full calculation pipeline implemented in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py):

```python

# Calculate base score from four categories (capped at individual maxima)

total_score = sum(
    min(cat["score"], cat["max"]) for cat in evaluation.scores.model_dump().values()
)

# Apply bonus points if present (max 20)

if getattr(evaluation, "bonus_points", None):
    total_score += evaluation.bonus_points.total

# Apply deductions if present

if getattr(evaluation, "deductions", None):
    total_score -= evaluation.deductions.total

# Enforce hard ceiling (100 base + 20 bonus max)

max_possible = sum(cat["max"] for cat in evaluation.scores.model_dump().values()) + 20
final_score = min(total_score, max_possible)

```

## Data Persistence and Auditing

The [`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py) module persists these modifiers for downstream analysis. Lines 715-721 write both `bonus_points.total` and `deductions.total` to CSV exports, enabling audit trails and statistical review of evaluator behavior across candidate pools.

## Summary

- **Bonus points** are defined in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) with a 0-20 point range and explanatory `breakdown`, added to the base score in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 58-60).
- **Deductions** subtract from the total based on the `Deductions` model's `total` field (lines 61-64), with `reasons` tracked for transparency.
- **Score capping** occurs after modifiers are applied, limiting the maximum to 120 points (100 base + 20 bonus) as implemented in lines 65-69.
- **Auditability** is maintained through CSV export in [`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py) (lines 715-721) and detailed breakdown strings stored in the evaluation objects.

## Frequently Asked Questions

### What is the maximum number of bonus points that can be added?

The `BonusPoints` model enforces a maximum of 20 points, as defined in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py). When combined with the 100-point base category maximum, the theoretical ceiling is 120 points before final capping.

### Can deductions reduce a score below zero?

The source code in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) does not explicitly enforce a floor at zero, though the final score typically remains positive because base category scores start above zero and deductions are usually smaller than the accumulated base score.

### Where are bonus and deduction values stored for reporting?

The [`transform.py`](https://github.com/interviewstreet/hiring-agent/blob/main/transform.py) module writes both values to CSV exports at lines 715-721, persisting `bonus_points.total` and `deductions.total` alongside the final score for offline analysis and auditing.

### How does the system handle evaluations without bonuses or deductions?

The [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) logic uses `hasattr` and truthiness checks (`if evaluation.bonus_points:`) to conditionally apply modifiers. If these fields are absent or null, the calculation proceeds with only the base category scores, skipping the addition or subtraction steps entirely.