Schema Language Mappings → TSON: Synthesis Report
Status: Non-normative report · Prepared against TSON 2026 Revision 32 (Working Draft) · Input to the Revision 33 development cycle. References to “planned additions” describe design directions under consideration for future revisions, not committed specification. Produced with AI assistance (Anthropic Claude) in collaboration with the TSON author; citations of external standards should be verified against primary sources before normative use. Schema source files are cited with the
.tnextension per the current draft-period convention (.tn1is reserved for the finalized version 1 release).
Consolidates five first-pass mapping reports — JSON Schema/OpenAPI, Protocol Buffers, ASN.1 (X.680–X.683), Apache Avro, and GraphQL SDL — into cross-cutting findings, a prioritized worklist for TSON Part 2, and a shared converter architecture. Companion documents: json-schema-to-tson-mapping-rev2.md, protobuf-to-tson-mapping.md, asn1-to-tson-mapping.md, avro-to-tson-mapping.md, graphql-to-tson-mapping.md.
1. The headline result
Five schema languages spanning four decades and every major design philosophy — constraint logic (JSON Schema), wire pragmatism (protobuf), telecom formalism (ASN.1), evolution-first streaming (Avro), capability graphs (GraphQL) — were mapped onto TSON Part 2, and the kernel survived untouched. Every construct in every language landed on existing machinery, an already-planned addition, or a principled decline. The additions the whole series requires of the kernel remain exactly the two identified in the first report’s design discussions: the discriminator token (#9) and the deferred sealed (#10). Everything else is core-library vocabulary, annotations, encoding rules, or converter logic.
This is the strongest available validation of the meta-kernel’s design discipline (ATOM/PRODUCT/SUM/REFERENCE, refinement, composition, groups) — and of the decision rule that produced it: the kernel grows only where a fact cannot be derived or a behavior cannot be desugared.
2. The sum-type taxonomy is complete
The single largest design effort — discriminated sums — closed with a four-cell taxonomy that absorbed all five languages with zero new constructs:
| Tag placement | Source constructs | TSON target |
|---|---|---|
| Sibling property value | OpenAPI discriminator; GraphQL __typename | choice + discriminator token, variants pin REQUIRED_FIXED field |
| Field presence (at-most-one) | protobuf oneof; GraphQL oneof input objects | OPTIONAL field group |
| Component label (exactly-one) | ASN.1 CHOICE | REQUIRED field group |
| Wrapper key | Avro union JSON encoding | single-group record (labelled sum) on the wire; choice in the model |
Supporting findings: the mapping table exists nowhere — it is derived from pinned constants everywhere (drift impossible by construction); GraphQL’s implicit __typename is the degenerate tag-value-equals-type-name case identified in report one; ASN.1’s UNIQUE and Apollo/Relay’s forced-__typename injection independently rediscovered the token’s two core rules (pairwise-distinct values; open sets require mandatory in-band tags). The envelope pattern (tag beside payload) recurs as ASN.1’s table-constrained open type and Kubernetes-style {kind, spec} objects, and needs no construct beyond the same machinery.
3. Cross-source scoreboard: what Part 2 must decide, weighted by evidence
| Item | Corroborating sources | Status |
|---|---|---|
| Null atom (#4) | All five: JSON type:"null"; proto wrappers; ASN.1 NULL; Avro null primitive + ["null",T] idiom; GraphQL nullable-by-default (the default case of the language) | Ship first. The present / |
| Recursion semantics (#3) | All five converters blocked: $ref cycles; recursive messages; ASN.1 recursive types; Avro linked structures; GraphQL type graphs (most aggressive) | The single blocking spec item. Adopt GraphQL’s admissibility hint: cycles legal where a participating position admits absence/ |
| Discriminator token (#9) | OpenAPI, GraphQL, ASN.1 table constraints; enables the JSON-encoding dispatch rule | Kernel addition, fully designed (pinned-FIXED, materialization, tag-as-assertion, no DEFAULT discriminators) |
| Rest field (#7) | JSON Schema mixed form only — no other language has openness of this shape | Confirms the design: openness is a JSON-Schema-ism, contained as an encoding transform, never a type |
sealed (#10) | Evidence sources now concrete: ASN.1 closed vs extensible object sets; GraphQL unions (sealed) vs interfaces (open) — one language with both cases as explicit constructs | Still deferred; Stage-5 census now has its corpus |
| Evolution/ | Avro resolution algebra (the spec); proto open enums, ASN.1 extensibility markers, Registry compat modes (the consumers) | New spec part: compatibility relation between revisions + rename bridge (alias-shaped) + explicit-never-silent transformation. Unifies all forward-compat report flags |
| Float specials policy | protojson ("NaN"/"Infinity" strings); ASN.1 REAL; Avro; GraphQL mandates finiteness | One core decision, four consumers; a finite facet has its first mandating source |
| Zone-less / | ASN.1 GeneralizedTime local forms; Avro local-timestamp-*, months/ | One core decision, decide once |
| Decimal precision/ | Avro decimal; ASN.1-adjacent; SQL inevitable | Tier 2 facet addition |
oid core type | ASN.1 (load-bearing: X.509/ | Earns its place as uuid did |
Injection semantics of REQUIRED_DEFAULT | Matches proto2 defaults, proto3 implicit presence, ASN.1 DEFAULT (DER omission), Avro resolution defaults, GraphQL input defaults; only JSON Schema default is non-injecting | Vindicated as designed; the converter rule flips per source, both directions principled |
| Declined constructs | not/if// | Verdict held across all appearances: negation and data-dependent validation stay out; drop-with-report. Avro and GraphQL had empty declined lists |
Regex dialect ledger (policy: translate when semantics-preserving; classify mechanically; fail loudly or annotate-unenforced; I-Regexp stays normative): ECMA-262 (JSON Schema — riskiest, backrefs/lookaround untranslatable) · RE2 (protovalidate — friendly, same exclusions as I-Regexp) · XSD-flavored (ASN.1 PATTERN — friendliest, I-Regexp is an XSD subset; verify the X.680 clause) · none (protobuf core, Avro, GraphQL). The ECMA-262 decision — keep the RFC, for cross-platform truth, ReDoS safety, and decidable pattern disjointness — held under every subsequent test and got easier each time.
4. Recurring structural patterns
Optionality regimes — three distinct systems, all landing on TSON’s five field states: membership-based (JSON Schema required[] → invert to ?), presence-discipline (proto editions LEGACY_REQUIRED/EXPLICIT/IMPLICIT → REQUIRED/?/~ zero), value-nullability (GraphQL T!/T → T / (T|null) — REQUIRED either way; input universe adds presence back). The field-state vocabulary needed no extension; only the discriminator materialization rule (#9c) modified it.
Closure — JSON Schema is the outlier: open-by-default objects required the triage rule, the rest field, and the strengthening argument. Protobuf messages, ASN.1 SEQUENCE (modulo ...), Avro records, and GraphQL types are all closed. Openness-as-forward-compatibility (proto open enums, ASN.1 extensibility markers, GraphQL interfaces) resolved uniformly: convert closed, flag per site, defer the real answer to the evolution part.
The strictness pitch inverts by source. Constraint-rich sources (JSON Schema, ASN.1) convert with strengthening (format→types, closure, alphabets→patterns). Constraint-poor sources (protobuf, Avro, GraphQL) convert faithfully-but-empty, and the pitch becomes “TSON refinements are the constraint layer your ecosystem never standardized” — with protovalidate as the one source-side constraint layer, mapping term-for-term. The invariant in both cases: rejected-by-source ⇒ rejected-by-conversion; the audit direction (silent weakening, not lossiness, is the failure mode) applies per keyword.
Lexical-layer decisions cluster. int64 spelling (protojson: string canonical; Avro JSON: number — document the contrast), bytes spelling (base64 / base64url / Avro’s ISO-8859-1 escape — flag the last loudly), map-key stringification (protojson-specified, reused for TSON’s non-text-key lowering), duplicate keys, big-number parsing (require lossless), name casing (protojson camelCase; GraphQL as-declared). Each converter resolves these as a canonical-strict profile with lenient flags, every leniency emitting a report line — the pattern generalized cleanly across all five.
5. Shared converter architecture
All five converters instantiate one pipeline:
- Dialect normalization front-end — OpenAPI 3.0→2020-12; proto2/proto3→editions features; ASN.1 1988/macros→current X.680; Avro pre-1.12 union-default rules; GraphQL spec-edition +
extendmerging + federation directives. All ambiguity is resolved here, before mapping. - Structural mapping — types → records/choices/groups/collections per the per-language tables; namespacing policy (one dotted-name decision serving proto packages, ASN.1 modules, Avro namespaces, GraphQL — still open, decide once).
- Constraint mapping — refinements from source constraints (ASN.1 subtypes, protovalidate, JSON Schema assertions) + upward mappings (format/logical-type/specifiedBy → core types) via shared tables.
- Provenance annotations —
@field_number,@tag,@wire,@reserved,@branch_order,@alias-bridge,@version_bracket,@proto_name,@graphql_id: wire/evolution metadata, uniformly non-semantic, uniformly preserved. - The report — a product surface, not a log: machine-readable drop/strengthen/flag entries (schema for it is a Stage-1 deliverable); aggregation required (ASN.1 extensibility flags would otherwise drown signal); doubles as migration worksheet and strictness audit.
Per-encoding canonical-strict profiles hang off this pipeline (protojson, Avro JSON, JER when encodings return to scope, GraphQL’s strict-per-query/lenient-document × input/output grid), as does the one genuinely novel product identified: the GraphQL per-query compiler — (SDL, query) → response-contract TSON schema — which reuses the entire design and serves an unmet need.
6. Prioritized worklist
Part 2 (spec), in order:
- Value-space/lexical-space clause + binary collapse to
bytes+tokenre-grounding (§8) — first because it’s the one breaking change, cheapest before anything else lands, and the time/float decisions below depend on its vocabulary. - Recursion clause (blocks everything; steal the nullable-link admissibility rule).
- Null atom + §7.3 tightening (blocks Avro and GraphQL fidelity; five sources waiting).
- Tier 1–2 batch: annotations, numeric facets incl. finite + precision/scale,
unique_items, literal enums (after null),contains, time-type decisions (as value spaces, per §8),oid. - Discriminator token (#9) — designed; write the normative text.
- Rest field (#7) +
requiredKeys(#8). - Evolution/compatibility part — spec its vocabulary early (report-flag wording across converters depends on it) even if it ships late.
sealed(#10) — after the object-set/union census.
Converters (product), in order: JSON Schema/OpenAPI (largest on-ramp; hardest, already designed) → protobuf + protovalidate (fast follow; Registry-adjacent) → Avro + Registry compat checker (the Kafka wedge; needs items 1–2) → GraphQL input-universe, then per-query compiler → ASN.1 with an X.509 pilot (highest-prestige strictness demo; smallest audience). Thrift as a protobuf appendix; XSD only on vertical demand.
7. What was declined, and why it held
Across five languages, every appearance of negation (not, EXCEPT), general conditionals (if/then/else, CEL, CONSTRAINED BY, value-keyed dependencies), dynamic scoping ($dynamicRef), and constraint-intersection composition was declined with the same reasoning: each would trade TSON’s decidable disjointness and structural kind algebra for coverage of a constraint-logic fringe — and the fringe shrank with every report (two languages had empty declined lists). The drop-with-report policy converts these from silent losses into migration worksheets. The one construct that looked like it might force openness into the type system — additionalProperties — was instead dissolved into an encoding transform over a closed record, following the convergent design of every major OO serialization library. The boundary held everywhere it was tested, and the tests were adversarial: five languages, five philosophies, forty years of accumulated features.
8. Encoding-leakage audit: what the wire has smuggled into the type system
A separate audit, prompted by the binary types, with a crisp test: does a type distinguish different values, or different spellings of the same value? Two “types” related by a value-preserving bijection, differing only in how the value appears in some encoding, are one type plus a leak. Results, in descending order of concern:
Binary types — clear failure, with a live bug. base64, base32, hex, base64url as distinct core types are four spellings of one value space (octet strings). The leak has already bitten the mapping work: the JSON Schema report’s contentEncoding: base64 → base64-type mapping silently conflated “bytes, spelled as base64 in JSON” with “text that happens to contain base64” (which is what the JSON Schema annotation actually asserts) — and the two have different equality semantics: under the first, a base64 spelling and a hex spelling of the same octets are the same value; under the second, different strings. Fix: collapse to one bytes atom; each encoding defines its lexical form and canonical spelling (part-3 JSON: base64 canonical; Avro-JSON profile: its ISO-8859-1 escape — demoted from mapping wart to lexical rule of one profile); non-default spellings become a non-semantic @spelling annotation consumed by codecs, invisible to the resolver — the same architectural slot as @json_discriminator. Payoff beyond hygiene: content addressing becomes encoding-independent — hashes over canonical value spaces mean the same data hashes identically across TSON text, JSON, and any future binary encoding, which is what hash-pinning should mean and currently doesn’t if base64 ≠ hex.
token vs text — second leak, subtler. If token means “text matching the identifier grammar of TSON’s text encoding” (unquoted-spelling privilege), a lexical convenience of one encoding has become a type. Fix: re-ground token as pattern-refined text (a value-space restriction — passes the test); unquoted-ness becomes a lexical rule of the text encoding that happens to apply to matching values. Same surface syntax; JSON encoding needs no token story; enum semantics stop depending on part 1’s tokenizer.
Time types and uuid — leaky iff defined lexically. If core datetime is “an RFC 3339 string,” then GeneralizedTime, protojson Timestamp, and JER spellings are different types rather than alternative lexical forms of one value — and every time-type ◑ in the five reports worsens. Fix: define instants, calendar values, and durations as abstract value spaces with RFC 3339 as the text/JSON lexical form; the queued zone-less and calendar-duration decisions belong here and are legitimate type distinctions (genuinely different value spaces). uuid: a 128-bit value space with a canonical text spelling — which makes a future binary encoding’s 16-byte form the same value, hashing identically.
Sized integers — pass, conditionally. int8…uint256 are value-space restrictions if defined as range-refinement aliases (int8 => integer ^ {min: -128, max: 127}), not storage widths; one sentence in core should say so, because “8-bit” in a definition is the leak. float32/float64 are ruled acceptable: IEEE value sets are genuinely distinct value spaces (representable sets, signed zero, NaN), not spellings of number — though the queued float-specials decision is exactly this boundary dispute (is NaN a value or an encoding artifact?); settle them together.
Already handled correctly by the architecture — evidence the pattern works: the !variant tag (out-of-band annotation with per-encoding presence rules — the entire discriminator design was this leak managed properly), the _ absent sentinel (per-encoding carriers, flagged since report one), and the @ordered-family markers (correctly non-semantic).
Deliverable: a short normative clause in Part 1 or 2 — “Types denote value spaces. Encodings define lexical spaces and one canonical form per type. Equality, ordering, refinement, disjointness, and content addressing are defined over value spaces only.” — turning the audit test into a standing conformance rule. Draft-stage cost: one breaking rename (the binary collapse) plus definition rewording; post-freeze cost: much worse. Cheap now, expensive later.
Caveats
- All five reports target the 2026 Revision 32 working draft plus planned-but-unfrozen additions; every “designed” item above is a design intention until Part 2 lands it.
- Per-report caveats (version-precise citations, the X.680 PATTERN lineage check, dialect edition pins) carry forward; this synthesis does not repeat them but does not supersede them.
- Several design threads that inform this synthesis were settled in design review but are not yet published as reports: the per-format encoding/profile-document architecture (encoding decisions as separate, hashable TSON documents wrapping a pinned schema), the interface-layer scoping study (abstract operations vs. transport bindings), and OpenAPI-specific vocabulary amendments to the JSON Schema report (see that report’s addendum). Where this document cites those conclusions, the reports are the pending artifacts.
- Market-share and sequencing judgments (§6 converter order) are informed estimates, not measured data.
- The empty-kernel-ask result is evidence of design fit, not proof of completeness: the sixth language (SQL DDL and CDDL are the plausible candidates) gets to try to break it.