Skip to content

Commit 6c83be3

Browse files
committed
docs(spec): adopt shallow last-wins merge for x-gts-traits and add ADR-0004
- Replace immutable-once-set rule in README §9.7 with shallow descendant-last-wins merge along the $id chain. - Lean on standard JSON Schema 'const' in x-gts-traits-schema as the publisher's lock mechanism; no GTS-specific immutability rule. - Refresh worked example to show natural override plus const-based lock. - Add ADR-0004 documenting drivers, alternatives (no-merge, immutable-once-set, RFC 7396, per-property keyword), decision, and conformance expectations. Signed-off-by: Aviator 5 <ai.agent.tor@gmail.com>
1 parent 4b320a1 commit 6c83be3

2 files changed

Lines changed: 439 additions & 8 deletions

File tree

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1506,23 +1506,25 @@ Given an inheritance chain `S₀ → S₁ → … → Sₙ`:
15061506
- **Immutable defaults:** `default` values declared in an ancestor's `x-gts-traits-schema` MUST NOT be changed by a descendant's `x-gts-traits-schema`. If a descendant redeclares a trait property with a different `default`, schema validation MUST fail.
15071507

15081508
- **Trait value merge**
1509-
- The registry MUST build an *effective traits object* by collecting all `x-gts-traits` objects encountered in the chain (left-to-right).
1510-
- **Immutable-once-set:** Once a trait key is assigned a concrete value by a schema in the chain, **no descendant may override it**. If a descendant's `x-gts-traits` provides a different value for a key already set by an ancestor, schema validation MUST fail. Providing the **same** value is permitted (idempotent).
1511-
- Defaults declared in the effective trait schema SHOULD be used as normal JSON Schema defaults to produce a complete effective traits object.
1509+
- The registry MUST build an *effective traits object* by walking the type's `$id` chain root → leaf and applying each layer's `x-gts-traits` via shallow object assignment: for any top-level key declared at multiple layers, the value from the layer closer to the type wins (descendant-last-wins). Object-valued traits are replaced wholesale; nested fields of an ancestor's object trait are not preserved when a descendant declares the same top-level key. Authors who want per-field composability SHOULD model traits as separate top-level properties rather than as nested objects.
1510+
- Defaults declared in the effective trait-schema SHOULD be applied (per ADR-0003 materialization) for top-level keys not present in the chain-merged object.
1511+
- A publisher who wants a trait value to be **locked** across all descendants of a base type SHOULD declare `"const": <value>` for that property in `x-gts-traits-schema`. A descendant attempting to override the value will fail the standard JSON Schema validation that runs against the effective trait-schema (per the Completeness check below). No GTS-specific "immutability" rule is required — `const` is the mechanism.
1512+
- A descendant MAY redeclare a trait value with the same value the ancestor already declared (idempotent restatement).
1513+
- See [`adr/0004-x-gts-traits-merge-strategy.md`](adr/0004-x-gts-traits-merge-strategy.md) for the rationale.
15121514

15131515
- **Validation**
15141516
- **Completeness check** (registration-time): For types whose `x-gts-abstract` is not `true`, the registry MUST verify that the *materialized* effective traits object validates against the effective trait-schema using standard JSON Schema validation. "Materialized" means: defaults declared in the effective trait-schema for properties not present in the chain-merged effective traits object are substituted in before validation. If validation fails — in particular, if a `required` property of the effective trait-schema has no chain-assigned value and no default — registration MUST fail. For types with `x-gts-abstract: true`, this completeness check is skipped; descendants are expected to close any unresolved required traits. See [`adr/0003-x-gts-traits-completeness.md`](adr/0003-x-gts-traits-completeness.md) for the rationale.
15151517
- If the effective trait schema cannot be satisfied (e.g., contradictory constraints introduced across the chain), schema validation MUST fail.
1516-
- If a descendant attempts to override a trait value already set by an ancestor with a different value, schema validation MUST fail.
15171518

1518-
**Example — immutable trait override (failure):**
1519+
**Example — descendant override and `const` lock:**
15191520

15201521
Consider a 3-level chain: `base → audit_event → most_derived_event`.
15211522

1522-
- `audit_event` sets `x-gts-traits.topicRef` to `gts.x.core.events.topic.v1~x.core._.audit.v1`
1523-
- `most_derived_event` attempts to set `x-gts-traits.topicRef` to `gts.x.core.events.topic.v1~x.core._.notification.v1`
1523+
- `base.x-gts-traits-schema.properties.indexed.const = true` — the publisher locks `indexed`.
1524+
- `audit_event.x-gts-traits` sets `topicRef = gts.x.core.events.topic.v1~x.core._.audit.v1`.
1525+
- `most_derived_event.x-gts-traits` sets `topicRef = gts.x.core.events.topic.v1~x.core._.notification.v1`.
15241526

1525-
Validation of `most_derived_event` MUST fail because `topicRef` was already set by `audit_event` and the new value differs.
1527+
Effective traits for `most_derived_event`: `{ "indexed": <chain-derived true>, "topicRef": ".../notification.v1" }`. The override of `topicRef` is permitted (last-wins). If `most_derived_event` also tried to set `"indexed": false`, registration would fail — not because of a GTS-specific immutability rule, but because the materialized effective traits object would not satisfy the `const: true` constraint declared on `indexed` in the effective trait-schema.
15261528

15271529
These rules are intentionally aligned with existing JSON Schema composition semantics and GTS schema chaining practices.
15281530

0 commit comments

Comments
 (0)