AFI parity: close all 35 gaps — every adapter wires every AFI-common capability

The conformance board (tests/afi/test_capability_parity.py) is now fully green:
90 capability cells + 4 meta-locks + 3 codegen byte-parity = 97 passed. The
gaps the prose table used to launder as "Django-only" / "out of scope" are
wired, against the pinned-spec model (single-authored spec, byte-identical
conformance across languages) — never per-language reimplementation.

FastAPI — edge_manifest + PSR (logic single-sourced in mizan_core.manifest),
WebSocket RPC (/ws/ through the shared dispatch), SSR (the framework-agnostic
SSRBridge relocated to mizan_core.ssr; Django rides it from there), Shapes
(SQLAlchemy projection, same declaration surface as django-readers), Forms
(Pydantic schema/validate/submit).

Rust (Axum + Tauri + cores/mizan-rust) — X-Mizan-Invalidate header, auth=
enforcement, origin HMAC cache, edge manifest + PSR, WebSocket handler / IPC
subscription channel, multipart upload, SSR bridge, Shapes, Forms; JWT/MWT
mint+verify and cache-key derivation byte-pinned to the Python reference
(cache_keys_pin, token_pin, invalidate_header_pin).

TypeScript — a KDL IR emitter byte-identical to the Python build_ir (so a TS
backend can feed the codegen — the largest gap), multipart upload, session-init,
WebSocket transport, SSR bridge, JWT/MWT mint (pinned to Python), Shapes, Forms.

Verified in the merged tree: core 25, fastapi 74, django 353/21-skip,
mizan-rust (incl. cross-language pins) green, axum 10, tauri 8, mizan-ts 103/2-skip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 13:44:35 -04:00
parent 58d2cb2848
commit 6c5f6f1fba
81 changed files with 9893 additions and 463 deletions

View File

@@ -0,0 +1,269 @@
"""
Shapes behavior — the genuine capability behind the `shapes` probe.
Proves the SQLAlchemy binding has the same Shape declaration surface and
projection/diff semantics as the Django `django-readers` binding:
- `Shape[Model]` resolves the mapped model + PK from the generic arg;
- scalar annotations project columns, Shape-typed annotations project relations;
- `.query(session, *stmt_fns, **relation_stmt)` flat / nested / scoped;
- nested loads stay flat (selectinload, not N+1);
- `.diff()` / `.diff_many()` detect field changes + nested created/updated/deleted.
"""
from __future__ import annotations
import pytest
from sqlalchemy import ForeignKey, create_engine, event
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column, relationship
from mizan_fastapi.shapes import Diff, NestedDiff, Shape
# ─── Mapped models ────────────────────────────────────────────────────────────
class Base(DeclarativeBase):
pass
class Publisher(Base):
__tablename__ = "publisher"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
country: Mapped[str]
authors: Mapped[list["Author"]] = relationship(back_populates="publisher")
class Author(Base):
__tablename__ = "author"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
bio: Mapped[str] = mapped_column(default="")
publisher_id: Mapped[int] = mapped_column(ForeignKey("publisher.id"))
publisher: Mapped[Publisher] = relationship(back_populates="authors")
books: Mapped[list["Book"]] = relationship(back_populates="author")
class Book(Base):
__tablename__ = "book"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str]
is_published: Mapped[bool] = mapped_column(default=True)
author_id: Mapped[int] = mapped_column(ForeignKey("author.id"))
author: Mapped[Author] = relationship(back_populates="books")
# ─── Shapes (declaration surface identical to the Django adapter) ──────────────
class FlatAuthorShape(Shape[Author]):
id: int | None = None
name: str
class FlatBookShape(Shape[Book]):
id: int | None = None
title: str
is_published: bool
class BookCardShape(Shape[Book]):
id: int | None = None
title: str
is_published: bool
author: FlatAuthorShape # single nested FK
class AuthorCardShape(Shape[Author]):
id: int | None = None
name: str
bio: str
books: list[FlatBookShape] = [] # list nested reverse-FK
class PublisherDetailShape(Shape[Publisher]):
id: int | None = None
name: str
authors: list[AuthorCardShape] = [] # 3-level nesting
# ─── Fixtures ──────────────────────────────────────────────────────────────────
@pytest.fixture
def session():
engine = create_engine("sqlite://")
Base.metadata.create_all(engine)
with Session(engine) as s:
pub = Publisher(name="Orbit", country="UK")
ann = Author(name="Ann Leckie", bio="Imperial Radch", publisher=pub)
devi = Author(name="Devi Pillai", bio="", publisher=pub)
ann.books = [
Book(title="Ancillary Justice", is_published=True),
Book(title="Provenance", is_published=False),
]
s.add_all([pub, ann, devi])
s.commit()
yield s
# ─── Declaration ────────────────────────────────────────────────────────────────
def test_shape_resolves_model_and_pk():
assert FlatAuthorShape._model is Author
assert FlatAuthorShape._pk_field == "id"
def test_flat_shape_has_no_nested():
assert FlatAuthorShape._nested == {}
assert FlatAuthorShape._field_names == ["id", "name"]
def test_single_nested_detected():
assert BookCardShape._nested == {"author": FlatAuthorShape}
def test_list_nested_detected():
assert AuthorCardShape._nested == {"books": FlatBookShape}
# ─── Query ──────────────────────────────────────────────────────────────────────
def test_flat_query_projects_fields(session):
authors = FlatAuthorShape.query(session)
assert len(authors) == 2
assert {a.name for a in authors} == {"Ann Leckie", "Devi Pillai"}
def test_query_with_stmt_fn_filters(session):
authors = FlatAuthorShape.query(session, lambda s: s.where(Author.name == "Ann Leckie"))
assert [a.name for a in authors] == ["Ann Leckie"]
def test_single_nested_fk_projected(session):
books = BookCardShape.query(session, lambda s: s.where(Book.title == "Ancillary Justice"))
assert len(books) == 1
assert books[0].author.name == "Ann Leckie"
def test_list_nested_reverse_fk_projected(session):
authors = AuthorCardShape.query(session, lambda s: s.where(Author.name == "Ann Leckie"))
assert len(authors) == 1
assert {b.title for b in authors[0].books} == {"Ancillary Justice", "Provenance"}
def test_empty_nested_list(session):
authors = AuthorCardShape.query(session, lambda s: s.where(Author.name == "Devi Pillai"))
assert authors[0].books == []
def test_three_level_nesting(session):
pubs = PublisherDetailShape.query(session)
assert len(pubs) == 1
leckie = next(a for a in pubs[0].authors if a.name == "Ann Leckie")
assert len(leckie.books) == 2
def test_relation_stmt_scopes_nested_load(session):
authors = AuthorCardShape.query(
session,
lambda s: s.where(Author.name == "Ann Leckie"),
books=lambda s: s.where(Book.is_published.is_(True)),
)
assert [b.title for b in authors[0].books] == ["Ancillary Justice"]
assert all(b.is_published for b in authors[0].books)
def test_nested_query_stays_flat(session):
"""selectinload keeps the projection at O(depth) queries, not N+1."""
counter = {"n": 0}
@event.listens_for(session.bind, "after_cursor_execute")
def _count(*args):
counter["n"] += 1
AuthorCardShape.query(session)
# one query for authors + one selectin for books
assert counter["n"] == 2
# ─── Diff ─────────────────────────────────────────────────────────────────────
def test_diff_no_changes(session):
book = session.query(Book).filter_by(title="Ancillary Justice").one()
shape = FlatBookShape(id=book.id, title="Ancillary Justice", is_published=True)
d = shape.diff(session)
assert d.is_new is False
assert d.changed == {}
def test_diff_detects_field_change(session):
book = session.query(Book).filter_by(title="Ancillary Justice").one()
shape = FlatBookShape(id=book.id, title="Ancillary Justice (rev)", is_published=True)
d = shape.diff(session)
assert d.changed["title"] == "Ancillary Justice (rev)"
def test_diff_new_item(session):
shape = FlatBookShape(id=None, title="Elantris", is_published=True)
d = shape.diff(session)
assert d.is_new is True
assert "title" in d.changed
def test_diff_nonexistent_pk_raises(session):
shape = FlatBookShape(id=999999, title="Ghost", is_published=False)
with pytest.raises(LookupError):
shape.diff(session)
def test_nested_diff_created_updated_deleted(session):
author = session.query(Author).filter_by(name="Ann Leckie").one()
books = sorted(author.books, key=lambda b: b.title)
# keep one (updated), drop one (deleted), add one (created)
shape = AuthorCardShape(
id=author.id,
name="Ann Leckie",
bio="Imperial Radch",
books=[
FlatBookShape(id=books[0].id, title="Ancillary Justice REWRITTEN", is_published=True),
FlatBookShape(id=None, title="Ancillary Sword", is_published=True),
],
)
d = shape.diff(session)
assert len(d.books.updated) == 1
assert len(d.books.created) == 1
assert len(d.books.deleted) == 1
def test_diff_strict_nested_access_raises_on_typo(session):
author = session.query(Author).filter_by(name="Ann Leckie").one()
shape = FlatAuthorShape(id=author.id, name="Ann Leckie")
d = shape.diff(session)
with pytest.raises(AttributeError):
_ = d.bookz
with pytest.raises(KeyError):
d.nested("bookz")
def test_diff_many_batches(session):
books = session.query(Book).all()
items = [FlatBookShape(id=b.id, title=b.title + "!", is_published=b.is_published) for b in books]
results = FlatBookShape.diff_many(session, items)
assert len(results) == len(books)
assert all("title" in d.changed for _, d in results)
def test_diff_many_mixed_new_and_existing(session):
book = session.query(Book).first()
items = [
FlatBookShape(id=book.id, title=book.title, is_published=book.is_published),
FlatBookShape(id=None, title="Brand New", is_published=False),
]
results = FlatBookShape.diff_many(session, items)
assert sum(1 for _, d in results if d.is_new) == 1
assert sum(1 for _, d in results if not d.is_new) == 1