Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Python
__pycache__/
*.pyc

# Virtual env
.venv/
env/

# Database
*.db

# OS
.DS_Store
161 changes: 118 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,118 @@
# Candidate Assessment: Spec-Driven Development With Codegen Tools

This assessment evaluates how you use modern code generation tools (for example `5.2-Codex`, `Claude`, `Copilot`, and similar) to design, build, and test a software application using a spec-driven development pattern. You may build a frontend, a backend, or both.

## Goals
- Build a working application with at least one meaningful feature.
- Create a testing framework to validate the application.
- Demonstrate effective use of code generation tools to accelerate delivery.
- Show clear, maintainable engineering practices.

## Deliverables
- Application source code in this repository.
- A test suite and test harness that can be run locally.
- Documentation that explains how to run the app and the tests.

## Scope Options
Pick one:
- Frontend-only application.
- Backend-only application.
- Full-stack application.

Your solution should include at least one real workflow, for example:
- Create and view a resource.
- Search or filter data.
- Persist data in memory or storage.

## Rules
- You must use a code generation tool (for example `5.2-Codex`, `Claude`, or similar). You can use multiple tools.
- You must build the application and a testing framework for it.
- The application and tests must run locally.
- Do not include secrets or credentials in this repository.

## Evaluation Criteria
- Working product: Does the app do what it claims?
- Test coverage: Do tests cover key workflows and edge cases?
- Engineering quality: Clarity, structure, and maintainability.
- Use of codegen: How effectively you used tools to accelerate work.
- Documentation: Clear setup and run instructions.

## What to Submit
- When you are complete, put up a Pull Request against this repository with your changes.
- A short summary of your approach and tools used in your PR submission
- Any additional information or approach that helped you.
# Transaction Review Service

This is a backend service that accepts transaction requests and decides whether to approve, review, or reject them based on predefined business rules.


It was built using a spec-driven workflow, where each feature was defined before implementation and validated with tests.

## What It Does
- Accepts transaction data through an API
- Applies deterministic rules to evaluate the risk of the transaction
- Stores transactions in a SQLite database
- Allows retrieval and filtering of transactions
- Provides a summary of decisions

## Tech Stack
- FastAPI
- SQLAlchemy + SQLite
- Pydantic
- Pytest + TestClient

## Run Locally
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload
```

Use /docs to explore endpoints.

API: `http://127.0.0.1:8000`
Docs: `http://127.0.0.1:8000/docs`

SQLite database file is created at `transactions.db`.

## Run Tests
```bash
pytest -q
```

## Main Endpoints
- `POST /transactions` - create and evaluate a transaction
- `GET /transactions` - list transactions (optional `?decision=approved|review|rejected`)
- `GET /transactions/{transaction_id}` - fetch one transaction by ID
- `GET /transactions/summary` - return aggregate counts by decision

## Request Fields (`POST /transactions`)
- `account_id` (string)
- `amount` (number, must be `> 0`)
- `country` (string)
- `available_balance` (number)
- `account_status` (string)
- `transaction_type` (string)
- `new_payee` (boolean)


## How To Submit A Transaction
Send a `POST` request to `/transactions` with JSON body.

Valid example:
```json
{
"account_id": "acct-100",
"amount": 250.0,
"country": "US",
"available_balance": 1000.0,
"account_status": "active",
"transaction_type": "card",
"new_payee": false
}
```

Invalid example (`amount <= 0`):
```json
{
"account_id": "acct-100",
"amount": 0,
"country": "US",
"available_balance": 1000.0,
"account_status": "active",
"transaction_type": "card",
"new_payee": false
}
```

Expected error response:
```json
{
"detail": "amount must be greater than 0"
}
```

## Decision Rules

### Rejected
- `account_status != "active"`
- `amount > available_balance`

### Review (only if not rejected)
- `amount >= 5000`
- `country != "US"`
- `new_payee == true` and `amount >= 1000`
- `transaction_type == "wire"` and `amount >= 2000`

### Approved
- no rules triggered

## Response Shape
- `POST /transactions` and retrieval endpoints return:
- `id` (int)
- `decision` (`approved` | `review` | `rejected`)
- `reasons` (list of strings)

## Error Behavior
- Invalid body/field types: `422`
- `amount <= 0`: `422` with `"amount must be greater than 0"`
- Invalid decision filter: `422`
- Unknown transaction ID: `404` with `"Transaction not found."`
46 changes: 46 additions & 0 deletions SPECS/decision-evaluation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Feature Spec: Decision Evaluation

## Goal
Evaluate a submitted transaction and return a deterministic decision (`approved`, `review`, or `rejected`) along with clear reasons.

## Scope
- In:
- Applying rule-based decision logic
- Returning decision outcomes and reasons
- Out:
- Machine learning or AI-based decision logic
- Dynamically configurable rules

## Decision Logic

### Rejection Rules (highest priority)
A transaction is rejected if any of the following are true:
- account_status is not "active"
- amount is greater than available_balance
Note: Invalid inputs (e.g., amount <= 0) are handled at the API validation layer and return a 422 error. The decision engine only evaluates valid transactions.

### Review Rules
If the transaction is not rejected, it is marked for review if any of the following are true:
- amount >= 5000
- country is not "US"
- new_payee is true AND amount >= 1000
- transaction_type is "wire" AND amount >= 2000

### Approval Rule
A transaction is approved only if none of the rejection or review rules apply.

## Output
- The system returns:
- decision: one of `approved`, `review`, `rejected`
- reasons: a list of human-readable explanations for triggered rules

## Acceptance Criteria
- [X] Inactive accounts are rejected
- [X] Transactions exceeding available balance are rejected

- [X] Large transactions are marked for review
- [X] International transactions are marked for review
- [X] New payee transactions meeting threshold are marked for review
- [X] Wire transactions meeting threshold are marked for review
- [X] Transactions with no triggered rules are approved
- [X] Response includes decision reasons when rules are triggered
23 changes: 23 additions & 0 deletions SPECS/decision-filtering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Feature Spec: Decision Filtering

## Goal
Allow users to filter transactions by decision outcome.

## Scope
- In:
- Filtering by decision type
- Out:
- Complex multi-field search

## Behavior
- The system supports filtering transactions by decision:
- approved
- review
- rejected
- Invalid filter values return an error

## Acceptance Criteria
- [X] Filtering by approved returns only approved transactions
- [X] Filtering by review returns only review transactions
- [X] Filtering by rejected returns only rejected transactions
- [X] Invalid filter values return an error
16 changes: 16 additions & 0 deletions SPECS/health-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Feature Spec: Health Check

## Goal
Provide a simple endpoint to verify that the service is running.

## Endpoint
GET /

## Response
{
"message": "Transaction Review Service is running. See /docs for API usage."
}

## Acceptance Criteria
- [X] GET / returns status 200
- [X] Response includes a message indicating the service is running
29 changes: 29 additions & 0 deletions SPECS/transaction-retrieval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Feature Spec: Transaction Retrieval

## Goal
Allow users to retrieve stored transactions and their decisions.

## Scope
- In:
- Retrieving all transactions
- Retrieving a transaction by ID
- Out:
- Pagination
- Sorting

## Behavior

### Retrieve All Transactions
- Returns all stored transactions
- Includes decision and reasons for each

### Retrieve by ID
- Returns a single transaction by ID
- If the ID does not exist, return a not-found error

## Acceptance Criteria
- [X] User can retrieve all transactions
- [X] User can retrieve a transaction by ID
- [X] Returned transactions include decision and reasons
- [X] Non-existent IDs return a not-found error

39 changes: 39 additions & 0 deletions SPECS/transaction-submission.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Feature Spec: Transaction Submission

## Goal
Allow users to submit a transaction request for evaluation.

## Scope
- In:
- Accepting transaction input via API
- Validating required fields and types
- Storing the transaction
- Out:
- Updating or deleting transactions
- Authentication and authorization

## Input Requirements
Each transaction must include:
- account_id (string)
- amount (number)
- country (string)
- available_balance (number)
- account_status (string)
- transaction_type (string)
- new_payee (boolean)

## Validation Rules
- All required fields must be present
- amount must be greater than 0
- Field types must be correct

## Behavior
- Valid transactions are stored with a unique ID
- Invalid requests return an error response

## Acceptance Criteria
- [X] Valid transaction is accepted and stored
- [X] Stored transaction includes a unique ID
- [X] Missing required fields return an error
- [X] Invalid field types return an error
- [X] Non-positive amounts are rejected
25 changes: 25 additions & 0 deletions SPECS/transaction-summary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Feature Spec: Transaction Summary

## Goal
Provide a summary of stored transactions.

## Scope
- In:
- Aggregating transaction counts by decision
- Returning total transaction count
- Out:
- Modifying transactions
- Advanced analytics or historical trends

## Requirements
- The system must provide a summary endpoint for transactions.
- The summary must include:
- total transaction count
- approved transaction count
- review transaction count
- rejected transaction count

## Acceptance Criteria
- [X] User can retrieve transaction summary
- [X] Summary includes total, approved, review, and rejected counts
- [X] Summary values accurately reflect stored transaction data
7 changes: 0 additions & 7 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,7 +0,0 @@
# TODO

## Refactor Proposals
-

## New Feature Proposals
-
21 changes: 21 additions & 0 deletions app/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from collections.abc import Generator

from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, Session, sessionmaker

DATABASE_URL = "sqlite:///./transactions.db"

engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


class Base(DeclarativeBase):
pass


def get_db() -> Generator[Session, None, None]:
db = SessionLocal()
try:
yield db
finally:
db.close()
Loading