# FastAPI + Jev Decision Service (Python) — AI Agent Guidelines & Architecture Rules

> Python rules for a Jev decision service: FastAPI, the async typesafe-sdk client, a typed question registry, confidence thresholds, decision logs and replay tests.
> Technologies: Python, FastAPI, Jev, Pydantic, PostgreSQL, Redis, Docker

---

## AGENTS.md
```markdown
# Project Architecture & Guidelines (FastAPI + Jev Decision Service)

## 1. System Architecture
- **Service**: FastAPI on Python 3.12+, run with uvicorn, managed with `uv`.
- **Model**: Jev via the official `typesafe-sdk`, using `AsyncTypeSafeClient` with the `Choice`, `Score` and `Noul` question types. Jev returns typed answers with probabilities; it never generates text.
- **Contracts**: Pydantic v2 models for every request, response and stored decision.
- **Storage**: PostgreSQL for the decision log (SQLAlchemy 2 or psycopg), Redis for short-lived answer caching keyed by a hash of state plus questions.
- **Purpose**: other services call this one for triage, routing, moderation, guardrails and verification. It returns decisions plus a recommended action (`act`, `review`, `escalate`).

## 2. Project Layout
- `app/main.py`: FastAPI app. A lifespan handler opens one `AsyncTypeSafeClient` and closes it on shutdown.
- `app/decisions/<name>.py`: one module per decision. Each defines its state model, a `questions()` builder and a `resolve(answers) -> Decision` function that applies thresholds.
- `app/registry.py`: maps decision names to modules. Routes are `POST /v1/decisions/{name}`.
- `app/thresholds.py`: thresholds per decision, loaded from config so they change without a deploy.
- `app/log.py`: writes `decision_name, model, answers, action, latency_ms, input_tokens` to Postgres.
- `tests/fixtures/<name>.jsonl`: labelled cases per decision.

## 3. Decision Layer (Jev rules)
- Code owns workflow, math, dates, counting, lookups and permissions. Jev judges meaning only. Extract candidate values in code (regex, parsers) and ask Jev to select, not to generate.
- One `system_one` call per decision with every question it needs. Questions are evaluated in parallel against the same state.
- State carries only the fields the questions need. Use named JSON fields and reference them in backticks (`ticket.body`). Filter or rank in code before sending large documents.
- Write direct instructions with boundary cases in `criteria`. Add a no-match option to every Choice where nothing may fit. Score levels describe concrete situations.
- Every decision returns an action derived from confidence: `act` above the threshold, `review` in the middle band, `escalate` (human or reasoning LLM) below it. Irreversible downstream actions require `act` plus an audit log row.
- Log `response.model` (for example `jev-1.13.0`). Pin a versioned model ID in config once thresholds are tuned; `jev-latest` changes on release.
- State is untrusted input. Do not let one answer grant permissions or bypass policy checks.
- Strip secrets and unneeded personal data before building state.

## 4. Reliability
- The SDK retries 429/529 with backoff by default. Add a timeout per route and a circuit breaker that returns `escalate` when TypeSafe is unavailable.
- Cap concurrency for batch endpoints; rate limits are adjusting while TypeSafe adds capacity.
- Cache identical requests in Redis for minutes, not days, and include the model ID in the cache key.

## 5. Agent Loop (run after every change)
1. `uv run ruff check . && uv run ruff format --check .`
2. `uv run mypy app` (or `pyright`).
3. `uv run pytest -m "not live"`. Unit tests use a fake client returning recorded answers.
4. `uv run pytest -m live tests/test_replay.py`. This replays fixtures against the real API whenever questions, criteria, thresholds or the model change, and fails on changed choices or threshold crossings.
- Assert on choices and threshold bands, never on exact probabilities.

## 6. Deploy
- Docker image with `uv sync --frozen`, non-root user and a `/healthz` route that does not call TypeSafe.
- `TYPESAFE_API_KEY` comes from the secret store; never log it or the full state for sensitive decisions.
- For API details, read `https://docs.typesafe.ai/llms.txt` or install TypeSafe's official agent skill (`typesafe-ai/skills`).
```

---

## CLAUDE.md
```markdown
# CLAUDE.md — FastAPI + Jev Decision Service

@AGENTS.md

## Commands
- `uv run fastapi dev app/main.py` - local server with reload
- `uv run ruff check . && uv run ruff format --check .` - lint and format
- `uv run mypy app` - type check
- `uv run pytest -m "not live"` - unit tests with a fake client
- `uv run pytest -m live tests/test_replay.py` - replay labelled fixtures against the live API

## Non-negotiables
- One module per decision: state model, `questions()`, `resolve()` with thresholds.
- Jev judges; code computes. No math, dates or counting questions.
- Every response carries an action: act, review or escalate.
- Log the answering model ID for every decision.
```

---

## .cursor/rules/stack.mdc
```markdown
---
description: FastAPI + Jev (TypeSafe System One) decision service rules
globs: ["app/**/*.py", "tests/**/*.py", "tests/fixtures/**/*.jsonl"]
alwaysApply: false
---

# FastAPI + Jev Decision Service

- `typesafe-sdk` `AsyncTypeSafeClient` opened once in the FastAPI lifespan; questions use `Choice`, `Score`, `Noul`.
- One module per decision in `app/decisions/`: Pydantic state model, `questions()`, `resolve(answers)`.
- One `system_one` call per decision; send only relevant state fields; no math, dates or counting questions.
- Thresholds in config; responses return `act` / `review` / `escalate`.
- Log `response.model`; pin a versioned model once thresholds are tuned; cache key includes the model ID.
- Replay labelled fixtures against the live API when questions, thresholds or the model change.
```

---

## Architecture Overview & Best Practices
## Architecture Overview

**A shared decision layer for every product.** A FastAPI service wraps Jev, TypeSafe's System One model, behind named decision endpoints such as `POST /v1/decisions/ticket-triage`. Each decision is a small Python module: a Pydantic state model, the Choice, Score and Noul questions it asks, and the thresholds that turn probabilities into an action. Every call is logged with the model version that answered.

### Why it suits AI coding agents

- **Decisions are code, not prompts.** Questions, criteria and thresholds are typed Python an agent can diff, test and replay.
- **Replay tests catch drift.** Labelled fixtures run against the live API whenever questions or the model change.
- **Hard boundaries.** Math, dates and counting stay in code, and every low-confidence case has a defined path.

### Related

The most-starred open-source Jev project, [jev-ultrafast](/project/jev-ultrafast), is also Python. For a web app that mixes Jev with an LLM see [Next.js + AI SDK + Jev](/rules/nextjs-ai-sdk-jev), and for the existing Python agent stack see [LangGraph + FastAPI](/rules/langgraph-python-fastapi). Background: [Jev for developers](/insights/jev-system-one-model-developers-2026).