Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 159 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
> **VERSION**: GTS specification draft, version 0.6
> **VERSION**: GTS specification draft, version 0.7

# Global Type System (GTS) Specification

Expand Down Expand Up @@ -90,6 +90,7 @@ See the [Practical Benefits for Service and Platform Vendors](#51-practical-bene
| 0.4 | Clarify some corner cases - tokens must not start with digit, uuid5, minor version semantic |
| 0.5 | Added Referece Implmenetation recommendations (section 9) |
| 0.6 | Introduced well-known/anonymous instance term; defined field naming implementation recommendations |
| 0.7 | BREAKING: require $ref value to start with 'gts://'; strict rules for schema/instance distinction |


## 1. Motivation
Expand Down Expand Up @@ -321,7 +322,7 @@ A third-party vendor (ABC) registers a derived event type for order placement:
"$id": "gts://gts.x.core.events.type.v1~abc.events.order_placed.v1~", // define a new event type derived from the base event type
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" }, // inherit base event schema
{ "$ref": "gts://gts.x.core.events.type.v1~" }, // inherit base event schema
{
"properties": {
"typeId": { "const": "gts.x.core.events.type.v1~abc.orders.order_placed.v1~" },
Expand Down Expand Up @@ -456,6 +457,16 @@ However, an **instance/object** may be represented in two common ways:
- `gts.x.core.events.topic.v1~x.commerce._.orders.v1.0`
- Field naming: typically `id` (alternatives: `gtsId`, `gts_id`).

Example:

```json
{
"id": "gts.x.core.events.topic.v1~x.commerce._.orders.v1.0",
"name": "orders",
"description": "Order lifecycle events topic"
}
```

- **Anonymous instance**: used for runtime-created objects where a globally meaningful name is not required (events/messages, DB rows, audit records, etc.).
- Recommended: use an opaque identifier as `id` (typically a UUID) and store the associated GTS **type identifier** separately (e.g., in a `type` field).
- Example (anonymous event instance):
Expand All @@ -464,6 +475,15 @@ However, an **instance/object** may be represented in two common ways:

This split is common in event systems: **topics/streams** are often well-known instances, while individual **events** are anonymous. See `./examples/events` and the field-level recommendations in section **9.1**.

Example:

```json
{
"id": "7a1d2f34-5678-49ab-9012-abcdef123456",
"type": "gts.x.core.events.type.v1~x.commerce.orders.order_placed.v1.0~",
"occurredAt": "2025-09-20T18:35:00Z"
}
```

## 4. GTS Identifier Versions Compatibility

Expand Down Expand Up @@ -620,7 +640,7 @@ This section demonstrates how different types of schema changes affect compatibi
"$id": "gts://gts.x.core.events.type.v1~x.api.users.create_request.v1.0~",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~" },
{
"properties": {
"payload": {
Expand All @@ -644,7 +664,7 @@ This section demonstrates how different types of schema changes affect compatibi
"$id": "gts://gts.x.core.events.type.v1~x.api.users.create_request.v1.1~",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~" },
{
"properties": {
"payload": {
Expand Down Expand Up @@ -687,7 +707,7 @@ This section demonstrates how different types of schema changes affect compatibi
"$id": "gts://gts.x.core.events.type.v1~x.commerce.orders.order_placed.v1.0~",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~" },
{
"properties": {
"payload": {
Expand All @@ -712,7 +732,7 @@ This section demonstrates how different types of schema changes affect compatibi
"$id": "gts://gts.x.core.events.type.v1~x.commerce.orders.order_placed.v1.1~",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~" },
{
"properties": {
"payload": {
Expand Down Expand Up @@ -756,7 +776,7 @@ This section demonstrates how different types of schema changes affect compatibi
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"allOf": [
{"$$ref": "gts.x.core.events.type.v1~"},
{"$$ref": "gts://gts.x.core.events.type.v1~"},
{
"type": "object",
"required": ["type", "payload"],
Expand All @@ -780,7 +800,7 @@ This section demonstrates how different types of schema changes affect compatibi
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"allOf": [
{"$$ref": "gts.x.core.events.type.v1~"},
{"$$ref": "gts://gts.x.core.events.type.v1~"},
{
"type": "object",
"required": ["type", "payload"],
Expand Down Expand Up @@ -916,7 +936,7 @@ Now, let's define the audit event schema for vendor `X` event manager:
"title": "Audit Event, derived from Base Event",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~" },
{
"type": "object",
"properties": {
Expand Down Expand Up @@ -947,7 +967,7 @@ Then, let's define the schema of specific audit event registered by vendor `ABC`
"title": "Vendor ABC Custom Purchase Audit Event from app APP",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~x.core.audit.event.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~x.core.audit.event.v1~" },
{
"type": "object",
"properties": {
Expand Down Expand Up @@ -1124,6 +1144,22 @@ It is recommended to put the GTS **type identifier** into the JSON Schema `$id`

Implementation note: GTS itself defines the canonical identifier string starting with `gts.`. When `$id` is expressed as `gts://...`, implementations should trim the `gts://` prefix and treat the remainder as the canonical GTS identifier for validation, comparison, and registry keys. The `gts://` prefix exists only to make `$id` URI-compatible.

**JSON Schema (`$ref`)**

It is recommended to make GTS schema references in JSON Schema `$ref` URI-compatible the same way as `$id`, by prepending the `gts://` prefix when `$ref` points at a GTS schema identifier:

```json
{
"allOf": [
{ "$ref": "gts://gts.x.core.events.type.v1~" }
]
}
```

Note: local JSON Schema references (e.g. `"$ref": "#/definitions/Foo"`, `"$ref": "#/$defs/Foo"`) are JSON Schema compliant and remain valid. The `gts://` recommendation applies only when `$ref` targets a GTS schema identifier.

Implementation note: When `$ref` is expressed as `gts://...`, implementations should trim the `gts://` prefix and treat the remainder as the canonical GTS identifier for resolution, validation, comparison, and registry keys. The `gts://` prefix exists only to make `$ref` URI-compatible.

**JSON instances (well-known vs anonymous)**

- **Well-known instances (named)**: recommended to use a GTS identifier in the `id` field (alternatives: `gtsId`, `gts_id`). Prefer a chained identifier so the **left segment(s)** define the schema/type automatically, and the **rightmost** segment is the instance name.
Expand Down Expand Up @@ -1276,7 +1312,119 @@ Result: ❌ NO MATCH (different major versions)

## 11. JSON and JSON Schema Conventions

It is advisable to include instance GTS identifiers in a top-level field, such as `id`. However, the choice of the specific field name is left to the discretion of the implementation and can vary from service to service.
### 11.1 Global rules: schema vs instance, normalization, and document categories

This section defines recommendations for how GTS-aware systems interpret JSON documents. The rules describe the concepts; the exact field names used for instance IDs and instance types are **implementation-defined** and may be **configuration-driven** (different systems may look for identifiers in different fields).

#### Rule A — Schema vs instance discriminator

**A JSON document is a schema if and only if it contains a top-level `$schema` field.**

- If `$schema` is present → the document MUST be treated as a **schema**.
- If `$schema` is absent → the document MUST be treated as an **instance**.

This discriminator MUST be applied before any ID parsing heuristics.

#### Rule B — GTS schema `$id` normalization

For GTS schemas (documents with `$schema`), it is recommended that `$id` is URI-compatible by using:
- `$id: "gts://<canonical-gts-id>"`

Implementations MUST normalize this by stripping the `gts://` prefix when extracting/returning the canonical GTS identifier. The `gts://` prefix exists only to make `$id` URI-compatible.

#### Rule C — JSON document categories

Implementations MUST clearly distinguish the following **five** categories of JSON documents:

1. **GTS entity schemas**
- Have `$schema`
- Have `$id` starting with `gts://` and the remainder is a valid **GTS type identifier** (ends with `~`)
- Example:

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "gts://gts.x.core.events.type.v1~",
"type": "object"
}
```

2. **Non‑GTS schemas**
- Have `$schema`
- Do not have a valid GTS `$id`
- Handling is **implementation-defined** (ignore vs error depending on API context)
- Example:

```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/schemas/order.json",
"type": "object"
}
```

3. **Instances of unknown / non‑GTS schemas**
- No `$schema`
- Schema/type cannot be determined (no acceptable schema/type reference field found, or the field value is not a valid GTS ID)
- Handling is **implementation-defined** (ignore vs error depending on API context)
- Example:

```json
{
"id": "123",
"payload": { "foo": "bar" }
}
```

4. **Well-known GTS instances (named)**
- No `$schema`
- Instance is identified by a **GTS instance identifier** (often a chain) stored in an implementation-chosen instance-ID field
- The schema/type is derived from the **left segment(s)** of the chain
- Example (well-known topic/stream instance):

```json
{
"id": "gts.x.core.events.topic.v1~x.commerce._.orders.v1.0",
"name": "orders"
}
```

> NOTE: In this specification, an instance identifier is a GTS identifier **without** the trailing `~` (i.e., it does not name a schema/type).
> Some systems may still accept an `id` field or it's equivalent that contains a **type/schema** identifier (ending with `~`) and treat it as a *schema reference* rather than an *instance identifier*.
> This behavior is **not defined by the GTS spec** and is entirely **implementation-specific / configuration-driven**.

5. **Anonymous GTS instances**
- No `$schema`
- Instance `id` is opaque (typically UUID)
- Schema/type is provided separately via an implementation-chosen schema/type field (e.g., `type`, `gtsType`, `gts_type`)
- Example (anonymous event instance):

```json
{
"id": "7a1d2f34-5678-49ab-9012-abcdef123456",
"type": "gts.x.core.events.type.v1~x.commerce.orders.order_placed.v1.0~",
"occurredAt": "2025-09-20T18:35:00Z"
}
```

> NOTE: In this specification, a type identifier is a GTS identifier **with** the trailing `~`.
> Some systems may still accept a `type` field or it's equivalent that contains an **instance** identifier (not ending with `~`). This behavior is **not defined by the GTS spec** and is entirely **implementation-specific / configuration-driven**.


#### ID and type-field heuristics (implementation-defined)

For **instances** (documents without `$schema`), implementations typically apply heuristics in this order:

1. **Try instance ID fields** (commonly `id`, then aliases like `gtsId`, `gts_id`):
- If the value is a valid GTS identifier, treat it as a **well-known instance** and derive `schema_id` from the chain (everything up to and including the last `~`).
- Otherwise treat it as an **anonymous instance** ID value.
2. **For anonymous instances**, determine the schema/type from a separate field (commonly `type`, or aliases like `schema`, `gtsType`, `gts_type`).

Different systems may choose different field names and priority orders via configuration. The examples below (and the `./examples/*` folders) use the common defaults: `id` for instance ID and `type` for instance type.

### 11.2 Examples

It is advisable to include instance identifiers in a top-level field such as `id`. However, the choice of the specific field name is left to the discretion of the implementation and can vary from service to service.

**Example #1**: **instance definition** of an object instance (event topic) that has an `id` field that encodes the object type (`gts.x.core.events.topic.v1~`) and identifies the object itself (`x.core.idp.events.v1`). In the example below it makes no sense to add an additional `type` field referring to the object schema because the `id` is already unique and there are no other event topics with the given id in the system:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "Event Instance Schema: order.placed",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~" },
{
"type": "object",
"required": ["type", "payload", "subjectType"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "Event Instance Schema: order.placed",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~" },
{
"type": "object",
"required": ["type", "payload", "subjectType"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"description": "Event instance schema for the user.created event type",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.events.type.v1~" },
{ "$ref": "gts://gts.x.core.events.type.v1~" },
{
"type": "object",
"required": ["type", "payload", "subjectType"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"title": "User",
"type": "object",
"allOf": [
{ "$ref": "gts.x.core.idp.contact.v1.0~" },
{ "$ref": "gts://gts.x.core.idp.contact.v1.0~" },
{
"type": "object",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"title": "MCP Tool with HTTP Outbound Capability",
"type": "object",
"allOf": [
{ "$ref": "gts.x.ai.mcp.tool.v1~" },
{ "$ref": "gts://gts.x.ai.mcp.tool.v1~" },
{
"type": "object",
"properties": {
Expand Down
4 changes: 2 additions & 2 deletions tests/test_op10_query_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ def register_wildcard_usecase_entities():
"description": "System message derived from v1.0",
"allOf": [
{
"$$ref": "gts.x.test10_llm.chat.message.v1.0~"
"$$ref": "gts://gts.x.test10_llm.chat.message.v1.0~"
}
]
})
Expand Down Expand Up @@ -557,7 +557,7 @@ def register_wildcard_usecase_entities():
"description": "User message derived from v1.1",
"allOf": [
{
"$$ref": "gts.x.test10_llm.chat.message.v1.1~"
"$$ref": "gts://gts.x.test10_llm.chat.message.v1.1~"
}
]
})
Expand Down
4 changes: 2 additions & 2 deletions tests/test_op11_attribute_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def test_start(self):
"$$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"allOf": [
{"$$ref": "gts.x.test11.events.type.v1~"},
{"$$ref": "gts://gts.x.test11.events.type.v1~"},
{
"type": "object",
"required": ["type", "payload"],
Expand Down Expand Up @@ -260,7 +260,7 @@ def test_start(self):
"$$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"allOf": [
{"$$ref": "gts.x.test11.events.type.v1~"},
{"$$ref": "gts://gts.x.test11.events.type.v1~"},
{
"type": "object",
"required": ["type", "payload"],
Expand Down
Loading