mirror of
https://github.com/azaion/ui.git
synced 2026-04-22 22:06:35 +00:00
134 lines
5.3 KiB
Markdown
134 lines
5.3 KiB
Markdown
# API Contract Template
|
||
|
||
A contract is the **frozen, reviewed interface** between two or more components. When task A produces a shared model, DTO, schema, event payload, or public API, and task B consumes it, they must not reverse-engineer each other's implementation — they must read the contract.
|
||
|
||
Save the filled contract at `_docs/02_document/contracts/<component>/<name>.md`. Reference it from the producing task's `## Contract` section and from every consuming task's `## Dependencies` section.
|
||
|
||
---
|
||
|
||
```markdown
|
||
# Contract: [contract-name]
|
||
|
||
**Component**: [component-name]
|
||
**Producer task**: [TRACKER-ID] — [task filename]
|
||
**Consumer tasks**: [list of TRACKER-IDs or "TBD at decompose time"]
|
||
**Version**: 1.0.0
|
||
**Status**: [draft | frozen | deprecated]
|
||
**Last Updated**: [YYYY-MM-DD]
|
||
|
||
## Purpose
|
||
|
||
Short statement of what this contract represents and why it is shared (1–3 sentences).
|
||
|
||
## Shape
|
||
|
||
Choose ONE of the following shape forms per the contract type:
|
||
|
||
### For data models (DTO / schema / event)
|
||
|
||
```[language]
|
||
// language-native type definitions — e.g., Python dataclass, C# record, TypeScript interface, Rust struct, JSON Schema
|
||
```
|
||
|
||
For each field:
|
||
|
||
| Field | Type | Required | Description | Constraints |
|
||
|-------|------|----------|-------------|-------------|
|
||
| `id` | `string` (UUID) | yes | Unique identifier | RFC 4122 v4 |
|
||
| `created_at` | `datetime` (ISO 8601 UTC) | yes | Creation timestamp | |
|
||
| `...` | ... | ... | ... | ... |
|
||
|
||
### For function / method APIs
|
||
|
||
| Name | Signature | Throws / Errors | Blocking? |
|
||
|------|-----------|-----------------|-----------|
|
||
| `do_x` | `(input: InputDto) -> Result<OutputDto, XError>` | `XError::NotFound`, `XError::Invalid` | sync |
|
||
| ... | ... | ... | ... |
|
||
|
||
### For HTTP / RPC endpoints
|
||
|
||
| Method | Path | Request body | Response | Status codes |
|
||
|--------|------|--------------|----------|--------------|
|
||
| `POST` | `/api/v1/resource` | `CreateResource` | `Resource` | 201, 400, 409 |
|
||
| ... | ... | ... | ... | ... |
|
||
|
||
## Invariants
|
||
|
||
Properties that MUST hold for every valid instance or every allowed interaction. These survive refactors.
|
||
|
||
- Invariant 1: [statement]
|
||
- Invariant 2: [statement]
|
||
|
||
## Non-Goals
|
||
|
||
Things this contract intentionally does NOT cover. Helps prevent scope creep.
|
||
|
||
- Not covered: [statement]
|
||
|
||
## Versioning Rules
|
||
|
||
- **Breaking changes** (field renamed/removed, type changed, required→optional flipped) require a new major version and a deprecation path for consumers.
|
||
- **Non-breaking additions** (new optional field, new error variant consumers already tolerate) require a minor version bump.
|
||
|
||
## Test Cases
|
||
|
||
Representative cases that both producer and consumer tests must cover. Keep short — this is the contract test surface, not an exhaustive suite.
|
||
|
||
| Case | Input | Expected | Notes |
|
||
|------|-------|----------|-------|
|
||
| valid-minimal | minimal valid instance | accepted | |
|
||
| invalid-missing-required | missing `id` | rejected with specific error | |
|
||
| edge-case-x | ... | ... | |
|
||
|
||
## Change Log
|
||
|
||
| Version | Date | Change | Author |
|
||
|---------|------|--------|--------|
|
||
| 1.0.0 | YYYY-MM-DD | Initial contract | [agent/user] |
|
||
```
|
||
|
||
---
|
||
|
||
## Decompose-skill rules for emitting contracts
|
||
|
||
A task is a **shared-models / shared-API task** when ANY of the following is true:
|
||
|
||
- The component spec lists it as a shared component (under `shared/*` in `module-layout.md`).
|
||
- The task's **Scope.Included** mentions any of: "public interface", "DTO", "schema", "event", "contract", "API endpoint", "shared model".
|
||
- The task is parented to a cross-cutting epic (`epic_type: cross-cutting`).
|
||
- The task is depended on by ≥2 other tasks across different components.
|
||
|
||
For every shared-models / shared-API task:
|
||
|
||
1. Create a contract file at `_docs/02_document/contracts/<component>/<name>.md` using this template.
|
||
2. Fill in Shape, Invariants, Non-Goals, Versioning Rules, and at least 3 Test Cases.
|
||
3. Add a mandatory `## Contract` section to the task spec that links to the contract file:
|
||
|
||
```markdown
|
||
## Contract
|
||
|
||
This task produces/implements the contract at `_docs/02_document/contracts/<component>/<name>.md`.
|
||
Consumers MUST read that file — not this task spec — to discover the interface.
|
||
```
|
||
|
||
4. For every consuming task, add the contract path to its `## Dependencies` section as a document dependency (not a task dependency):
|
||
|
||
```markdown
|
||
### Document Dependencies
|
||
- `_docs/02_document/contracts/<component>/<name>.md` — API contract produced by [TRACKER-ID].
|
||
```
|
||
|
||
5. If the contract changes after it was frozen, the producer task must bump the `Version` and note the change in `Change Log`. Consumers referenced in the contract header must be notified (surface to user via Choose format).
|
||
|
||
## Code-review-skill rules for verifying contracts
|
||
|
||
Phase 2 (Spec Compliance) adds a check:
|
||
|
||
- For every task with a `## Contract` section:
|
||
- Verify the referenced contract file exists at the stated path.
|
||
- Verify the implementation's public signatures (types, method shapes, endpoint paths) match the contract's Shape section.
|
||
- If they diverge, emit a `Spec-Gap` finding with High severity.
|
||
- For every consuming task's Document Dependencies that reference a contract:
|
||
- Verify the consumer's imports / calls match the contract's Shape.
|
||
- If they diverge, emit a `Spec-Gap` finding with High severity and a hint that either the contract or the consumer is drifting.
|