ADR-0011 — The ingest contract: Pydantic schemas for Analysis / WikiPage / Message / ProviderCapabilities¶
- Status: Accepted
- Date: 2026-06-28
- Sprint: v0.2
- Decider: solution-architect
- Invariants: I6 (one contract for all backends), I5 (WikiPage carries Obsidian-valid frontmatter), I8 (schemas drive D3)
- Related: ADR-0007, AC-F17-1, AC-F3-1, AC-F3-2; full field tables in v0.2-architecture.md §"Locked schemas"
- Resolves: AQ-v0.2-3 (exact Pydantic schemas)
Context¶
Analysis, WikiPage, Message, and ProviderCapabilities are referenced across AC-F17-1,
AC-F3-1, AC-F3-2 but never fully specified (AQ-v0.2-3). They are the contract between the
orchestrator and every provider; any divergence breaks all three backends. They must be frozen
before provider code begins.
Decision¶
The schemas live in backend/app/ingest/schemas.py (a new module, not models.py — these are
transport DTOs, not SQLAlchemy ORM rows). Analysis, WikiPage, Message,
SuggestedPage are Pydantic v2 BaseModels (they parse/validate provider output).
ProviderCapabilities and Usage are frozen dataclasses (internal descriptors, no external
parsing). PageType is a str-Enum with members
entity, concept, source, synthesis, comparison.
WikiPage.frontmatteris a typedWikiFrontmatterBaseModel (notdict[str, Any]) so the required keystype, title, sources, langare enforced by the schema itself, withextraallowed for future keys. This makes the validator (ADR-0007 §5) largely a re-use of Pydantic validation plus the non-empty-sourcesbusiness rule.WikiPage.contentis the Markdown body without the frontmatter block; the writer serializesfrontmatter+contentinto the final.mdfile. This keeps the body and the metadata independently inspectable and avoids double-parsing.Messageis the minimal{role: Literal["user","assistant","system"], content: str}— not the full Anthropic Message type — so it stays backend-neutral (I6). It is used only by the stubbedchat()in v0.2.
The exact, byte-level field tables (types, required/optional, constraints) are recorded in
docs/sprints/v0.2-architecture.md under "Locked schemas" and are the normative reference for
ai-agent-engineer. They are reproduced there rather than here to keep one source of truth for
the field list.
Consequences¶
- (+) AQ-v0.2-3 resolved; provider implementations and the validator share one typed contract.
- (+) Typed
WikiFrontmatterenforces I5 (Obsidian-valid frontmatter) at the schema boundary, so a malformed page cannot reach the writer. - (+) Backend-neutral
Messagekeepschat()from leaking Anthropic types into the ABC (I6). - (−) Two DTO styles (Pydantic for parsed I/O, dataclass for internal descriptors) is a minor inconsistency; justified by the parse-vs-descriptor distinction and avoids paying Pydantic validation cost on a pure capability read.