# How Bonus Points and Deductions Work in the Hiring Agent Scoring System

> Understand how bonus points and deductions work in the Hiring Agent scoring system. Learn how scores are calculated, capped, and adjusted by evaluators.

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

---

**Bonus points add up to 20 points to a candidate’s final score, while deductions subtract penalties from the cumulative total, with both values hard-capped between -20 and 120 according to the evaluator constraints.**

The Hiring Agent scoring system evaluates candidates by combining LLM-generated assessments with structured bonus points and deductions mechanisms. According to the interviewstreet/hiring-agent source code, the **ResumeEvaluator** processes resumes through a strict schema that defines how extra credit and demerits modify the final numeric outcome. This article explains the exact calculation logic, constraints, and file locations governing these score adjustments.

## LLM-Generated Evaluation Schema

The `ResumeEvaluator` class prompts the LLM with specific scoring criteria that must return a structured JSON response. This response must contain two dedicated objects: `bonus_points` and `deductions`. 

The `bonus_points` object requires a `total` field and a `breakdown` field, while the `deductions` object requires a `total` field and a `reasons` list. These schemas are strictly enforced through Pydantic models to ensure data integrity before score calculation begins.

## Data Model Constraints in models.py

The schema definitions and validation rules reside in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py), where Pydantic constraints enforce hard limits on both values.

### BonusPoints Structure

The `BonusPoints` model is defined in **[`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py)** between lines 31 and 34. It enforces the following constraints:

- **`total`**: Integer bounded between **0 and 20** (`ge=0, le=20`)
- **`breakdown`**: Textual explanation of why points were awarded

This 20-point ceiling represents the maximum additive boost available to any candidate.

### Deductions Structure

The `Deductions` model is defined in **[`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py)** between lines 36 and 41. It specifies:

- **`total`**: Non-negative integer (`ge=0`) that will be subtracted from the final score
- **`reasons`**: List of textual explanations for each penalty

Note that deductions are stored as positive values but are always subtracted during aggregation.

## Score Aggregation Logic in score.py

After the LLM returns an `EvaluationData` instance, the score calculation logic in **[`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py)** applies the adjustments. The specific handling occurs at lines 57-60 for bonuses and lines 62-64 for deductions.

```python

# Add category scores → total_score

# ...

# Add bonus points

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

# Subtract deductions

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

```

### Adding Bonus Points

The code checks for the existence of `bonus_points` on the evaluation object. If present, it adds the `total` value directly to the cumulative score derived from category assessments.

### Subtracting Deductions

Similarly, the logic validates the presence of `deductions` and subtracts the `total` value from the running total. This occurs after all category scores and bonuses have been summed.

## Final Score Constraints and Capping

After adding bonuses and subtracting deductions, the system applies hard limits to prevent extreme values. The maximum possible score equals the sum of all category maximums plus the full 20-point bonus ceiling. The minimum final score is **-20**.

These boundaries are defined as constants in **[`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py)** between lines 9 and 12. The final score is clamped to ensure it never exceeds 120 points and never drops below -20, regardless of the LLM’s output.

## Report Output Format

The user-visible evaluation report displays these adjustments transparently. The raw bonus total appears alongside its textual breakdown, showing exactly which criteria earned extra credit. Deductions render as negative amounts with a bulleted list of reasons, providing full auditability for the final score.

## Summary

- **Bonus points** are capped at 20 points and added to the cumulative category scores during final calculation.
- **Deductions** are positive values stored in the `Deductions` model that are subtracted from the total score in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py).
- **Final scores** are clamped between -20 and 120 as defined in [`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py) to maintain evaluation consistency.
- Both adjustments are stored in the `EvaluationData` model with full breakdowns and reasons for complete transparency.

## Frequently Asked Questions

### What is the maximum number of bonus points a candidate can receive?

The `BonusPoints.total` field in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) enforces a hard ceiling of **20 points** using Pydantic constraints (`ge=0, le=20`). The LLM cannot assign more than this value, and any attempt to exceed it fails validation at the schema level.

### How are deductions applied to the final score in Hiring Agent?

Deductions are stored as positive integers in the `Deductions.total` field. During the aggregation phase in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 62-64), the system subtracts this value from the cumulative score. The `reasons` list accompanies each deduction to explain why the penalty was applied.

### Where are the score limits defined in the Hiring Agent codebase?

The minimum final score of **-20** and maximum of **120** are defined as constants in **[`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py)** between lines 9 and 12. These values act as absolute floors and ceilings after all bonuses and deductions have been calculated.

### What happens if the LLM returns a bonus or deduction outside the allowed range?

The Pydantic models in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) validate inputs at ingestion time. Any `bonus_points.total` exceeding 20 or any negative `deductions.total` value triggers a validation error before the data reaches the calculation logic in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py), ensuring only compliant values enter the scoring pipeline.