Skip to content

Validation accepts annotations that storage cannot honour #1942

Description

@shcheklein

Umbrella for the validation/storage contract. Consolidates #1917, #1920, #1921, #1923, #1927, #1933, #1934 and #1935.

is_chain_type, or the model-field path that bypasses it, accepts an annotation. The storage layer then cannot honour it, and the failure surfaces somewhere else — at write, at read, or only after a reload in a fresh process. Nothing checks that validation and storage agree about what is supported.

Use cases

Accepted, then the write fails

Accepted, then the read is wrong or impossible

Accepted, then it vanishes

Accepted nowhere, though storage could manage it

  • is_chain_type rejects an enum at the root, so output={"k": Species} and a top-level dict[Species, int] raise UdfSignatureError — while the same enum works as a model field. The same disagreement pointing the other way.

This list is very likely incomplete

That is the main reason for one ticket rather than eight.

Every instance was found one at a time, over four review rounds, and in three of those the newly-found case was one a previous fix had introduced. Each was discovered because somebody named a specific spellingKey[str], Literal[1] | bool, type A0[T] = A1[T], a model defining __value__. None came from a systematic sweep, because there isn't one.

The surface is combinatorial: Annotated, NewType, PEP-695 aliases (bare, subscripted, defaulted, recursive), Literal (string, numeric, enum-valued, mixed), enums (str-mixin, int-mixin, plain, mixed-value), unions and Optional around any of those, nested in list/dict/tuple/a model, in key or value position, across four read routes and the write path.

Assume more instances exist. A change that ticks only the boxes above should be treated as incomplete.

What would actually close this

Not another round of point fixes:

  • one authority on "can this annotation be stored", consulted by is_chain_type and by model-field validation, so the two cannot drift;
  • a round-trip property test over generated annotations — declare, write, read by every route, reload in a fresh process, compare. This is the only thing that finds the cases nobody has thought to name, and it is the part that matters most;
  • for anything storage cannot honour, rejection at schema-build time naming the field, instead of a TypeError from the SQL layer or a silent degradation to Any.

A ~12-line detector was prototyped that catches every exotic annotation above (28 cases, no false positives on anything we want to keep), if loud rejection is the preferred direction.

Related

🤖 AI-assisted (Claude Code) — verify before relying on it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions