@terpjs/spec 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/catalog/backend/schemas_avoid_positional_tuples.json +3 -3
- package/corpus/backend/schemas_avoid_positional_tuples/compliant-02/modules/notes/schemas.py +2 -0
- package/corpus/backend/schemas_avoid_positional_tuples/violation-03/modules/notes/schemas.py +2 -0
- package/package.json +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.24.0
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "backend/schemas_avoid_positional_tuples",
|
|
3
3
|
"surface": "backend",
|
|
4
4
|
"title": "A schema field never crosses the wire as a positional tuple",
|
|
5
|
-
"intent": "A fixed-length tuple annotation (tuple[str, str], list[tuple[str, int]]
|
|
5
|
+
"intent": "A fixed-length tuple annotation (tuple[str, str], list[tuple[str, int]]) on a schema a client can see or send serialises into the contract as an array whose element types are positional (prefixItems, or the list form of items). Client generators do not agree on that shape: one emits the positional form and another the widened element array, so the two descriptions of the same field are structurally unrelated and the app cannot type its own calls against its own API — the failure surfaces at the call site as an opaque generic-instantiation mismatch, far from the field that caused it, and only with error truncation disabled. A fixed tuple is also a poor contract in its own right: the positions carry meaning that no name records. Name the shape instead — a nested model with named fields when the positions differ in meaning, or a homogeneous sequence when they do not. Scope is the positional shape only: a variadic tuple[X, ...] serialises byte-identically to list[X] — it is the immutable spelling of a homogeneous sequence, the natural annotation for a frozen value object — so it is compliant, and refusing it would force a source rewrite with zero wire effect. A fixed tuple nested inside one (tuple[tuple[str, int], ...]) is still refused.",
|
|
6
6
|
"layer": "static-bespoke",
|
|
7
7
|
"enforcement": [
|
|
8
8
|
{
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
],
|
|
19
19
|
"runtime": {
|
|
20
20
|
"applicability": "required",
|
|
21
|
-
"rationale": "The invariant survives into the running app: the generated OpenAPI document is the artifact the client is built from, so the boot-time contract validation walks
|
|
21
|
+
"rationale": "The invariant survives into the running app: the generated OpenAPI document is the artifact the client is built from, so the boot-time contract validation walks the generated document and refuses a positional array shape (prefixItems, or items as a list) fail-closed, reporting every offending location in one pass rather than raising on the first. Judging the document rather than the annotations is what makes the scope exact in both directions: a variadic tuple emits nothing positional and passes, while a tuple reaching the contract through a discriminated-union member, a type alias, a generic parameter, or a custom __get_pydantic_core_schema__ is in the document no matter what the annotation walk could see — and both halves name the same fix."
|
|
22
22
|
},
|
|
23
|
-
"reference": "Schema fields annotated with a nested BaseSchema model or a homogeneous list[...]; tuple[...] annotations on BaseSchema subclasses and on any class used as a route body or response_model are refused.",
|
|
23
|
+
"reference": "Schema fields annotated with a nested BaseSchema model or a homogeneous sequence (list[...], or the variadic tuple[X, ...] which serialises identically); fixed-length tuple[...] annotations on BaseSchema subclasses and on any class used as a route body or response_model are refused.",
|
|
24
24
|
"opt_out": "# arch-allow-schemas-avoid-positional-tuples: <reason>",
|
|
25
25
|
"guide_topic": "module",
|
|
26
26
|
"corpus": true
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terpjs/spec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "The Terp Standard — stack-neutral rule catalog, violation corpus, finding format, and refused-surface declaration (ADRs 0080/0081; packaged per ADR 0082, published per ADR 0086). Data only: consumers resolve the spec root via require.resolve('@terpjs/spec/package.json').",
|
|
5
5
|
"files": [
|
|
6
6
|
"VERSION",
|