# How Bonus Points and Deductions Are Handled in Hiring Agent’s Resume Evaluation

> Learn how Hiring Agent manages bonus points and deductions in resume evaluation. Understand score adjustments and the -20 to 120 final score range processed in score.py.

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

---

**Bonus points are additive rewards capped at 20 points, while deductions are subtractive penalties, both processed in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) after LLM evaluation and constrained within a -20 to 120 final score range.**

The `interviewstreet/hiring-agent` repository implements a structured scoring pipeline where an LLM evaluates resumes against specific criteria and returns extra rewards or penalties. These adjustments are formally defined in the data models, aggregated during score calculation, and displayed in the final candidate report.

## Evaluation Schema Definitions

The contract between the LLM evaluator and the scoring engine is defined in **[`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py)**, which mandates two specific objects in the JSON response: `bonus_points` and `deductions`.

### BonusPoints Model

The `BonusPoints` schema requires a `total` integer bounded between **0 and 20** (`ge=0, le=20`) and a `breakdown` list explaining the awards. According to the source code in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) (lines 31-34), this acts as a strictly limited additive boost to the candidate’s score.

### Deductions Model

The `Deductions` schema defined in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) (lines 36-41) contains a non-negative `total` (`ge=0`) and a `reasons` list. Unlike bonus points, this value represents a positive number that the scoring logic will later **subtract** from the cumulative score.

## Score Aggregation Logic

After the `ResumeEvaluator` receives the LLM’s `EvaluationData` response, the calculation moves to **[`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py)**, where category scores are first summed, then modified by these adjustments.

### Adding Bonus Points

The code checks for the presence of bonus points before applying them:

```python

# Add category scores → total_score

# ...

# Add bonus points

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

```

This logic appears in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 57-60), ensuring only valid bonus objects contribute to the total.

### Subtracting Deductions

Deductions are processed immediately after bonuses in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 62-64):

```python

# Subtract deductions

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

```

This operation treats the deduction `total` as a penalty value to be removed from the running score.

## Final Score Constraints

Once bonuses and deductions are applied, the aggregate is clamped to enforce business rules defined in **[`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py)** (lines 9-12). The final score:

- **Cannot exceed 120** (the sum of category maximums plus the 20-point bonus ceiling)
- **Cannot fall below -20** (the defined minimum final score)

This prevents edge cases where excessive deductions or bonuses might distort the evaluation unfairly.

## User-Visible Output

The final report renders these adjustments for human review:

- **Bonus points** display as the raw total alongside the textual breakdown from the LLM
- **Deductions** appear as a negative line item accompanied by the specific reasons provided by the evaluator

## Summary

- **Bonus points** are strictly bounded (0–20) and defined in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) (lines 31-34)
- **Deductions** are non-negative values stored in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) (lines 36-41) and subtracted during calculation
- **[`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py)** handles the arithmetic: lines 57-60 add bonuses, lines 62-64 subtract deductions
- Final scores are capped between -20 and 120 according to constraints in [`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py) (lines 9-12)
- Both adjustments appear with detailed explanations in the evaluation report

## Frequently Asked Questions

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

The `BonusPoints.total` field is constrained to a maximum of **20 points** with a minimum of 0, as enforced by the Pydantic model in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py). This hard limit prevents over-inflation of scores regardless of the LLM’s assessment.

### How are deduction values treated if the final score becomes negative?

The scoring logic in [`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py) implements a floor of **-20** for the final result. Even if cumulative deductions would mathematically push the score lower, the value is clamped to this minimum threshold before being recorded or displayed.

### Why does the Deductions model use a non-negative constraint if it represents a penalty?

The schema enforces `ge=0` (greater than or equal to zero) to ensure data integrity—the value stored is a positive magnitude of points lost. The sign conversion happens during aggregation in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py), where the code explicitly **subtracts** this positive value from the running total.

### Where can I modify the bonus point ceiling or deduction logic?

To adjust the 20-point bonus cap, edit the `le=20` validator in [`models.py`](https://github.com/interviewstreet/hiring-agent/blob/main/models.py) (lines 31-34). To change how deductions affect the total, modify the subtraction logic in [`score.py`](https://github.com/interviewstreet/hiring-agent/blob/main/score.py) (lines 62-64). Always verify that corresponding constraints in [`evaluator.py`](https://github.com/interviewstreet/hiring-agent/blob/main/evaluator.py) are updated to reflect new score boundaries.