test_return_window_expiredtest_return_window_expired
risk_score: null
decision: REJECTELECTRONICS allows 30 days and the request is 35 days after purchase, so it is rejected in step 1 and never scored.
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.
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:
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:
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):
Step 3: Decision + Reason
Choose a decision based on the calculated risk score from decision_bands.json.
Add reason only when the decision is MANUAL_REVIEW or REJECT.
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.
test_return_window_expiredtest_return_window_expired
risk_score: null
decision: REJECTELECTRONICS allows 30 days and the request is 35 days after purchase, so it is rejected in step 1 and never scored.
test_high_risk_reject_with_reasontest_high_risk_reject_with_reason
risk_score: 95
decision: REJECT
reason: HIGH_RISK_SCORE24 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.
run=== Running Return Routing Engine ===
Results written to: data/results.jsonlThe CLI routes every line of data/returns.jsonl and writes one result object per request to data/results.jsonl.
test_returnable_category_passes_to_scoringtest_returnable_category_passes_to_scoring
risk_score_present: true
decision: AUTO_APPROVE