# Schemas — Contract Schemas

## Overview

This module defines the data contracts between ENTR Core and tenant adapters. All schemas are Pydantic v2 models with `Field(description=...)` on every field for LLM consumability. The schemas enforce the structure of data flowing in both directions: Core → Adapter (payloads) and Adapter → Core (responses).

## Concepts

### Schema Ownership

ENTR Core is the **single source of truth** for all contract schemas. The lifecycle:

1. Schema changes originate in the ENTR Core repository
2. Changes are synchronized to this package
3. A CI diff check validates compatibility
4. Version bumps document schema changes in `CHANGELOG.md`

Adapters never define local copies of these schemas — always import from `entr_adapter_core.schemas`.

### Core-to-Adapter: DocumentPayload

`DocumentPayload` represents the structured data extracted from a document by Core, sent to the adapter's `/process` endpoint. Key concepts:

- **`data`** contains the extracted JSON with mappings already applied by Core — this is what adapters work with
- **`original_data`** preserves the pre-mapping data for comparison if needed
- **`applied_mappings`** is informational — it shows what Core changed, but adapters typically don't need it
- **`original_file_content_b64`** and **`original_file_name`** provide optional access to the raw file (e.g., for uploading to a target system)

### Adapter-to-Core: AdapterResponse

`AdapterResponse` is the standardized response shape that Core always receives, regardless of success or failure. Key concepts:

- **`status`** is one of three values: `success`, `failed`, `review_required`
- **`execution_logs`** is populated automatically by the flight recorder — handlers never set this
- **`review_suggestions`** enables the review workflow: structured field-level suggestions with proposed values and confidence scores
- **`failure_reason`** and **`failure_type`** provide structured error information on failure

### Retrieval Schemas

The retrieval schemas support the document retrieval workflow where adapters pull documents from external sources:

- **`RetrievalPayload`** — Core tells the adapter which source to query and from what date
- **`RetrievalResponse`** — Adapter returns a list of available documents (identifiers + filenames)
- **`DocumentDownloadPayload`** — Core requests a specific document by identifier
- **`DocumentDownloadResponse`** — Adapter returns the document content (base64-encoded)

## Patterns

### Status Flow

| Status | Meaning | When to Use |
|--------|---------|-------------|
| `success` | Document processed and delivered to target system | Normal completion |
| `failed` | Processing could not complete | Validation errors, target system errors, unrecoverable issues |
| `review_required` | Document needs human review before proceeding | Ambiguous matches, confidence below threshold, missing data |

### Review Suggestions Pattern

When a handler returns `review_required`, it should include structured suggestions:

```python
return AdapterResponse(
    document_id=payload.document_id,
    status=StatusEnum.REVIEW_REQUIRED,
    review_reason="Ambiguous product match",
    review_suggestions=[
        ReviewSuggestion(
            field_path="line_items.0.product_id",
            original_value="Widget-A",
            proposed_values=[
                ProposedValue(value="PROD-001", description="Widget Alpha (95% match)"),
                ProposedValue(value="PROD-002", description="Widget Apex (82% match)"),
            ],
            reason="Fuzzy match on product code",
        )
    ],
)
```

### Field Description Convention

Every schema field has a `Field(description=...)` annotation. This serves two purposes:
1. LLM consumability — Claude Code can read the descriptions directly from the schema code
2. API documentation — FastAPI auto-generates OpenAPI docs from these descriptions

## Guidance

### What Constitutes a Breaking Schema Change

| Change | Breaking? |
|--------|-----------|
| Removing a field | **Yes** — major version bump |
| Changing a field's type | **Yes** — major version bump |
| Renaming a field | **Yes** — major version bump |
| Adding a new optional field | No — minor version bump |
| Adding a new required field | **Yes** — major version bump |
| Changing a field's default value | Potentially — document in CHANGELOG |

See `CHANGELOG.md` for the full breaking change policy and migration path requirements.

### Removed Fields

The following fields were present in earlier contract versions and intentionally removed during the v1.0.0 schema consolidation:

- `meta`, `context`, `mappings`, `mapping_rules` from `DocumentPayload` — Core internals that adapters don't need
- `created_mappings`, `generated_mappings` from `AdapterResponse` — never used in practice

### Import Convention

Always import schemas from the package — never define local copies:

```python
from entr_adapter_core.schemas import DocumentPayload, AdapterResponse, StatusEnum
```

## Cross-References

- **API reference**: See docstrings and Pydantic `Field(description=...)` annotations in `payload.py`, `response.py`, and `retrieval.py` for complete field definitions, types, and defaults
- **Breaking change policy**: See `CHANGELOG.md` `Breaking Change Policy` section for the authoritative table
- **Handler integration**: See `handlers.md` for how handlers consume `DocumentPayload` and return `AdapterResponse`
- **Flight recorder**: See `logging.md` for how `execution_logs` is populated automatically
- **Retrieval flow**: See `connectors.md` for pre-built retrieval handlers that produce `RetrievalResponse` and `DocumentDownloadResponse`
