Return Routing Engine

10s 256 MB
Exclusive Normal+ 4.5
SystemsDebugging Amazon +1
Amazon OA ยท Question 2

The current return routing engine is broken. It sometimes approves requests that should fail policy checks, computes risk scores incorrectly, maps requests to the wrong decision, and misses or assigns the wrong reason. Your task is to fix the engine so it returns the correct decision, score, and reason for each return request.

Fix budget 12 lines. Stay under it and the patch counts as surgical. An edited line counts once. 2 of 15 files are editable: engine.cpp, parser.cpp. Submit also runs hidden tests.
Symptom 1

Approves requests that should fail policy checks

Open +40 XP
Observed
A 35-day ELECTRONICS return, against a 30-day window, is auto-approved and scored.
Expected
Past the window: decision REJECT, reason RETURN_WINDOW_EXPIRED, risk_score null.

Context

Product returns are a critical part of any E-commerce platform. Customers may request a return if a product doesn't meet their expectations.

When a return request is submitted, the platform must decide how to handle it. Some returns can be automatically approved, some must be sent for manual review, and others must be rejected outright.

This decision is not random. It is based on:

  • Return eligibility rules, for example, whether the category allows returns and whether the request is within the allowed time window, and
  • Risk signals, such as the customer's historical return behavior across related accounts.

Issue

The current return routing engine is broken. It sometimes approves requests that should fail policy checks, computes risk scores incorrectly, maps requests to the wrong decision, and misses or assigns the wrong reason.

Your task is to fix the engine so it returns the correct decision, score, and reason for each return request.

Configuration files

These files are pre-loaded at startup.

  • data/category_rules.json Defines which categories are returnable and the maximum allowed return window of that category (in days)
  • data/scoring_rules.json Defines how risk_score is calculated.
  • data/decision_bands.json Defines which final decision corresponds to each risk score band.

Account profiles

Input file: data/account_profiles.jsonl

One JSON object per line. Provides return history counts for known accounts.

Example:

{"account_id":"u101","return_history_count":2}
{"account_id":"u102","return_history_count":8}
{"account_id":"u103","return_history_count":4}

If an account is missing from this file, treat its return_history_count as 0.

Linked Accounts

Input file: data/account_links.jsonl

One JSON object per line. Customers may operate multiple accounts. These accounts are considered linked for risk evaluation purposes.

Example:

{"primary_account_id":"u101","related_account_id":"u102","link_type":"SHARED_SHIPPING_ADDRESS"}
{"primary_account_id":"u101","related_account_id":"u103","link_type":"SHARED_PAYMENT_METHOD"}

In this example, u101, u102, and u103 are all part of the same linked-account group.

Return requests

Input file: data/returns.jsonl

One JSON object per line. Each line is one return request.

Example:

{"request_id":"r101","account_id":"u101","category":"ELECTRONICS","days_since_purchase":12,"order_value_usd":250,"account_age_days":400}
{"request_id":"r102","account_id":"u220","category":"GROCERY","days_since_purchase":5,"order_value_usd":30,"account_age_days":10}

Output

Output file: data/results.jsonl

One JSON object per request.

Each output includes:

  • request_id: copied from input
  • risk_score: integer 0-100 if the request passed policy checks, otherwise null
  • decision: one of:
    • AUTO_APPROVE
    • MANUAL_REVIEW
    • REJECT
  • reason: required only when decision is MANUAL_REVIEW or REJECT; omit otherwise

Example:

{"request_id":"r101","risk_score":35,"decision":"AUTO_APPROVE"}
{"request_id":"r102","risk_score":null,"decision":"REJECT","reason":"CATEGORY_NON_RETURNABLE"}
{"request_id":"r103","risk_score":80,"decision":"REJECT","reason":"HIGH_RISK_SCORE"}

Step 1: Category checks

A product is returnable only if its category exists in data/category_rules.json and is marked returnable=true. If the category is missing or marked non-returnable, reject immediately: decision REJECT, reason CATEGORY_NON_RETURNABLE, risk_score null.

Compare days_since_purchase against the category's allowed_return_window_days. If days_since_purchase is strictly greater than the allowed window, reject immediately: decision REJECT, reason RETURN_WINDOW_EXPIRED, risk_score null.

Step 2: Risk score

For the request's account_id, compute total return history by summing the account's own return_history_count from data/account_profiles.jsonl and every linked account's return_history_count, following data/account_links.jsonl. A missing profile counts as 0, and an account with no links uses only its own profile.

For each rule (for example, account_age_days):

  • Read the attribute specified in the rule.
  • Identify the matching value range.
  • Add the corresponding points to the total.
  • Sum points for all the rules mentioned in scoring_rules.json.
  • Clamp the total to the range 0-100.
  • The resulting value is the final risk_score.

Step 3: Decision + Reason

Choose a decision based on the calculated risk score from decision_bands.json.

  • 0-49 - AUTO_APPROVE
  • 50-74 - MANUAL_REVIEW
  • 75-100 - REJECT

Add reason only when the decision is MANUAL_REVIEW or REJECT.

  • MANUAL_REVIEW -> MEDIUM_RISK_SCORE
  • REJECT -> HIGH_RISK_SCORE

Note: Refer to README.md for examples.

This environment

One line on stdin: run routes every request in data/returns.jsonl and writes data/results.jsonl, and the name of a test in tests/engine_test.cpp runs that test and prints one key: value line per field it checks. A request that never reached scoring prints risk_score: null, and a result with no reason prints reason: (none).

src/returns/engine.cpp and src/returns/parser.cpp are editable. The headers, the CLI, the files under data/ and tests/engine_test.cpp are read only, and README.md holds the full routing rules and the worked examples.

Tsuki, the tab under the editor, summarizes a file, explains a failing test, or finds where a field is computed. It does not write the fix and does not say whether your code passes.

Example 1
Input
test_return_window_expired
Output
test_return_window_expired
risk_score: null
decision: REJECT
Explanation

ELECTRONICS allows 30 days and the request is 35 days after purchase, so it is rejected in step 1 and never scored.

Example 2
Input
test_high_risk_reject_with_reason
Output
test_high_risk_reject_with_reason
risk_score: 95
decision: REJECT
reason: HIGH_RISK_SCORE
Explanation

24 returns across the linked group is 50 points, a 60-day-old account is 20, an $800 order is 25. 95 falls in the REJECT band, which carries HIGH_RISK_SCORE.

Example 3
Input
run
Output
=== Running Return Routing Engine ===
Results written to: data/results.jsonl
Explanation

The CLI routes every line of data/returns.jsonl and writes one result object per request to data/results.jsonl.

Loading editor...
Input
test_returnable_category_passes_to_scoring
Expected Output
test_returnable_category_passes_to_scoring
risk_score_present: true
decision: AUTO_APPROVE