Skip to content

feat(gts): single-pass type/payload validation and reusable schema helpers#103

Open
aviator5 wants to merge 4 commits into
GlobalTypeSystem:mainfrom
aviator5:gts-improvements
Open

feat(gts): single-pass type/payload validation and reusable schema helpers#103
aviator5 wants to merge 4 commits into
GlobalTypeSystem:mainfrom
aviator5:gts-improvements

Conversation

@aviator5

@aviator5 aviator5 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

API & store:

  • Split GtsStore::new(Option) into new() and with_reader(); add
    Default impl; update all call sites and tests to GtsStore::new()
  • Add validate_schema(type_id) -> ResolvedType, a self-contained resolved
    view (inlined schema, chain-merged effective traits, composed traits
    schema); export ResolvedType
  • validate_payload/validate_instance reuse the resolved view instead of
    re-deriving traits per call
  • try_resolve_schema_refs returns StoreError::CircularRef / UnresolvedRefs;
    resolve_schema_refs keeps unresolved refs in lenient output

Schema helpers:

  • New schema_refs module: extract_gts_refs + ExtractRefsError for
    discovering schema dependencies before resolution
  • schema_traits: extract inline_local_pointers (resolve JSON Pointer refs
    against the host doc); build_effective_traits / build_effective_traits_schema
    build the effective traits schema once
  • schema_compat: add merge_additional_properties_constraint, a closedness-
    preserving lattice so a permissive allOf overlay can never loosen a closed
    base; apply it in extract_effective_schema and refine compatibility comments
  • schema_modifiers: add validate_gts_keywords, a single structural gate for
    x-gts-final/abstract/traits keyword format + placement

Fixes:

  • x_gts_ref: a relative x-gts-ref resolving to a wildcard pattern is now
    accepted via GtsIdPattern, matching the absolute branch
  • entities: chained Type Schema doc fix (type_id is parent, None for
    standalone) plus coverage
  • gts-id: reject the gts: form and assert is_valid agrees on gts://

Summary by CodeRabbit

  • New Features
    • Added public extraction of external schema dependencies from JSON Schemas.
    • Exposed enriched resolved-type and reference-extraction utilities for downstream workflows.
  • Improvements
    • Tightened GTS identifier validation (including URI-prefix forms) and ref handling with clearer outcomes and safer resolution.
    • Reworked schema reference resolution and trait validation/materialization, including more consistent additionalProperties closedness across composed schemas.
    • Simplified store construction with reader-based initialization.
  • Bug Fixes
    • Improved x-gts-ref value/pattern matching, including recursion-depth safeguards and cycle termination.

- Derive Type Schema parent IDs from chained  values instead of  fallbacks.
- Validate x-gts-ref literals and matches through GtsIdPattern for malformed wildcard rejection and segment-aware version matching.

Signed-off-by: Aviator 5 <ai.agent.tor@gmail.com>
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@aviator5, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 29 minutes and 11 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9d7d3af9-f8c1-430f-81e4-64a8bca8b0b9

📥 Commits

Reviewing files that changed from the base of the PR and between fc35374 and 75e482c.

📒 Files selected for processing (9)
  • gts/src/entities.rs
  • gts/src/lib.rs
  • gts/src/ops.rs
  • gts/src/schema_refs.rs
  • gts/src/schema_resolver.rs
  • gts/src/schema_resolver_test.rs
  • gts/src/schema_traits.rs
  • gts/src/store.rs
  • gts/src/store_test.rs
📝 Walkthrough

Walkthrough

Refactors GtsStore construction with new() and with_reader(reader), overhauls StoreError with richer variants for instance/type/ref failures. Adds strict try_resolve_schema_refs and lenient resolve_schema_refs in a new schema_resolver module with DFS cycle/unresolved-ref tracking. Rewrites trait system via EffectiveTraits, materialize_traits_recursive with const-over-default precedence, and inline_local_pointers. Implements additionalProperties dominance lattice merging, new schema_refs extraction module, and GtsIdPattern-based x-gts-ref validation with depth-guarded pointer resolution. Updates ops validation flow with validate_gts_keywords and check_entity_traits enforcement.

Changes

GTS Schema Validation & Trait System Overhaul

Layer / File(s) Summary
Store API surface: constructor, ResolvedType, and error variants
gts/src/store.rs, gts/src/lib.rs
GtsStore::new() becomes argument-free and creates an empty store; GtsStore::with_reader(reader) replaces optional-reader construction. StoreError gains instance/type lookup variants (InstanceNotFound, InvalidEntity, InvalidTypeId) and explicit $ref failure modes (CircularRef, UnresolvedRefs). ResolvedType struct is added and re-exported. register/register_schema error handling uses new variants.
Schema refs extraction and URI validation module
gts/src/schema_refs.rs
New file adds ExtractRefsError (path-tracked InvalidRef with InvalidRefReason, TooDeep(64)) and extract_gts_refs function. Recursively traverses schemas collecting external $ref values, validates refs via GtsTypeId::try_new, normalizes gts:// prefixes and fragments, enforces depth limits, and deduplicates via BTreeSet. Comprehensive unit tests cover normalization, deduplication, depth enforcement, and error reporting.
Schema resolver: lenient vs. strict $ref resolution with cycle detection
gts/src/schema_resolver.rs, gts/src/schema_resolver_test.rs
New file implements SchemaProvider trait and SchemaResolver struct with resolve (lenient, preserving unresolvable external $refs) and try_resolve (strict, returning StoreError::CircularRef or UnresolvedRefs). Performs DFS cycle detection, normalizes gts:// refs, inlines GTS $defs, strips root-only and type-modifier keys when inlining non-root targets, and merges resolved targets with siblings via allOf. Test suite covers passthrough, inlining, composition, unresolved handling, and circular reference detection.
Store entity helpers and $ref URI validation
gts/src/store.rs
Adds helper methods get_schema_entity, get_schema_content, get_instance_entity with stricter schema-vs-instance validation and standardized StoreError mapping. validate_ref_uris validates $ref structure by delegating to extract_gts_refs.
Entity ID extraction from $id chain
gts/src/entities.rs
extract_type_ids now derives gts_id, instance_id, and selected_entity_field from $id with gts:// normalized. For chained IDs, type_id is derived from parent GtsId; standalone type schemas have type_id = None. Falls back to legacy extraction when $id path fails. Tests updated to assert new $id-chain semantics and removal of $schema-based type derivation.
additionalProperties closedness merging across allOf
gts/src/schema_compat.rs
New merge_additional_properties_constraint helper implements dominance lattice where false (closed) wins over true (open) and object values. extract_effective_schema uses this helper when reading top-level and allOf-accumulated additionalProperties, replacing prior last-wins behavior. Comment clarification and new test for false-wins across allOf branches.
Effective traits: build, materialize, validate, and inline local pointers
gts/src/schema_traits.rs, gts/src/store.rs
Introduces EffectiveTraits with validate(check_unresolved) method. build_effective_traits composes effective schema (with leaf dialect pinning) and materializes trait values. materialize_traits_recursive uses const > default precedence with rightmost-wins deduplication and recursive nested object materialization. inline_local_pointers resolves local #/... JSON Pointer $refs in-place. Store pipeline collects per-chain-level schemas, inlines local pointers, resolves external refs strictly, builds/materializes artifacts, and validates.
Schema keyword structural validation and trait closedness enforcement
gts/src/schema_modifiers.rs, gts/src/ops.rs
New validate_gts_keywords validates x-gts-final/x-gts-abstract/x-gts-traits placement by delegating to validate_schema_modifiers + validate_trait_placement. add_entity unconditionally calls it for schema entities before registration. Schema validation is conditional on validate flag: validate=false runs only $ref/x-gts-ref structure checks; validate=true runs full validation. New check_entity_traits exempts abstract types, requires merged trait values when schemas exist, and enforces additionalProperties: false on each resolved trait schema.
x-gts-ref pattern validation via GtsIdPattern and pointer recursion guards
gts/src/x_gts_ref.rs
New validate_value_matches_gts_pattern parses instance as GtsId and pattern as GtsIdPattern, then calls matches_pattern. validate_ref_pattern accepts resolved wildcard patterns for relative /... references via GtsIdPattern::try_new. resolve_pointer_inner depth-guards recursive pointer resolution. Old ad-hoc helpers are removed. Tests verify exact/wildcard/prefix matching, relative resolution, and cycle termination.
Test suite constructor migration and expanded coverage
gts/src/store_test.rs, gts-macros/tests/*, gts-id/src/gts_id.rs
All GtsStore::new(None) replaced with GtsStore::new(); reader construction migrated to GtsStore::with_reader(...); store_test.rs substantially expanded with strict vs. lenient resolver coverage, OP#12 schema-chain scenarios, OP#13 trait validation tests, and tightened error assertions. One test (test_gts_id_does_not_accept_uri_prefix) is removed. gts-id tests updated to assert rejection of gts:x... and gts:// forms.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GtsOps
  participant validate_gts_keywords
  participant GtsStore
  participant SchemaResolver
  participant schema_traits

  Client->>GtsOps: add_entity(entity, validate=true)
  GtsOps->>validate_gts_keywords: validate_gts_keywords(content)
  validate_gts_keywords-->>GtsOps: Ok / Err
  GtsOps->>GtsStore: register(entity)
  GtsStore-->>GtsOps: Ok(gts_id)
  GtsOps->>GtsStore: store.validate_schema(gts_id)
  GtsStore->>SchemaResolver: try_resolve_schema_refs(schema)
  SchemaResolver-->>GtsStore: Ok(resolved_schema) / Err(CircularRef/UnresolvedRefs)
  GtsStore->>schema_traits: build_effective_traits(schemas, traits)
  schema_traits->>schema_traits: inline_local_pointers(fragment, root)
  schema_traits->>schema_traits: materialize_traits_recursive(values)
  schema_traits-->>GtsStore: EffectiveTraits
  GtsStore->>schema_traits: EffectiveTraits::validate(check_unresolved)
  schema_traits-->>GtsStore: Ok / Err(Vec<String>)
  GtsStore-->>GtsOps: Ok(ResolvedType) / Err(StoreError)
  GtsOps->>GtsOps: check_entity_traits(gts_id)
  GtsOps-->>Client: Ok / Err(entity validation failure)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • GlobalTypeSystem/gts-rust#57: Both implement OP#13 schema traits validation by introducing/restructuring the schema_traits module and rewiring store+ops validation paths; this PR substantially rewrites what #57 first established.
  • GlobalTypeSystem/gts-rust#90: Both target overlapping gts-spec v0.12 logic in schema_traits.rs, schema_compat.rs, and schema_modifiers.rs around RFC 7396 trait merging, inherited closedness, and validate_trait_placement.
  • GlobalTypeSystem/gts-rust#55: Both modify schema_compat.rs to implement schema-vs-schema compatibility validation; this PR's additionalProperties closedness lattice merging directly builds on the foundation established by #55.

Suggested reviewers

  • Artifizer

Poem

🐇 Hopping through schemas with gts:// so bright,
The resolver now handles strict cycles and lenient refs right—what a sight!
EffectiveTraits bloom where const takes the lead,
additionalProperties: false fulfills every need,
Closedness preserved through lattice domination,
Depth-guarded pointers prevent stack ovation! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main changes: single-pass type/payload validation and reusable schema helpers, which aligns directly with the refactored validation flow and new helper modules introduced throughout the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter

codecov-commenter commented Jun 19, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 98.14077% with 28 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
gts/src/store.rs 94.38% 11 Missing ⚠️
gts/src/schema_resolver.rs 95.23% 7 Missing ⚠️
gts/src/schema_traits.rs 98.33% 5 Missing ⚠️
gts/src/ops.rs 99.03% 2 Missing ⚠️
gts/src/schema_compat.rs 92.85% 2 Missing ⚠️
gts/src/x_gts_ref.rs 99.06% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
gts/src/store.rs (2)

1199-1208: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Run x-gts-ref checks against the resolved schema.

JSON Schema validation uses the resolved schema, but Line 1244 passes the raw schema to XGtsRefValidator. Since that validator does not follow $ref, x-gts-ref constraints inside referenced schemas are skipped for registered instance validation; validate_payload already uses the resolved schema.

🐛 Proposed fix to retain the resolved schema for extension validation
-        let schema_with_internal_refs_resolved = self
+        let resolved_schema = self
             .try_resolve_schema_refs(&schema)
             .map_err(|e| StoreError::ValidationError(format!("Schema '{type_id}' has {e}")))?;
@@
         let schema_with_internal_refs_resolved =
-            Self::remove_x_gts_ref_fields(&schema_with_internal_refs_resolved);
+            Self::remove_x_gts_ref_fields(&resolved_schema);
@@
         // Validate x-gts-ref constraints
         let validator = crate::x_gts_ref::XGtsRefValidator::new();
-        let x_gts_ref_errors = validator.validate_instance(&obj.content, &schema, "");
+        let x_gts_ref_errors = validator.validate_instance(&obj.content, &resolved_schema, "");

Also applies to: 1243-1246

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gts/src/store.rs` around lines 1199 - 1208, The XGtsRefValidator should
validate against the resolved schema to ensure x-gts-ref constraints inside
referenced schemas are checked, not the raw schema. Pass the resolved schema
(the result of try_resolve_schema_refs) to the XGtsRefValidator instead of the
raw schema parameter. This will make x-gts-ref validation consistent with JSON
Schema validation which already uses the resolved schema, and ensure that
constraints in referenced schemas are properly validated during registered
instance validation.

543-611: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve passthrough allOf branches when flattening.

When any branch contributes properties, Line 611 returns the flattened schema but ignores resolved_all_of. That drops unresolved refs in lenient output and also loses non-mergeable constraints from sibling branches, silently weakening schemas.

🐛 Proposed fix to keep non-flattened branches
                             Value::Object(ref item_map) => {
                                 // If this item still has a $ref, keep it in allOf
                                 if item_map.contains_key("$ref") {
                                     resolved_all_of.push(resolved_item);
                                 } else {
+                                    let mut passthrough = item_map.clone();
                                     // Merge properties and required fields from resolved items
                                     if let Some(Value::Object(props_map)) =
                                         item_map.get("properties")
                                     {
+                                        passthrough.remove("properties");
                                         for (k, v) in props_map {
                                             merged_properties.insert(k.clone(), v.clone());
                                         }
                                     }
                                     if let Some(Value::Array(req_array)) = item_map.get("required")
                                     {
+                                        passthrough.remove("required");
                                         for v in req_array {
                                             if let Value::String(s) = v
                                                 && !merged_required.contains(s)
                                             {
                                                 merged_required.push(s.to_owned());
@@
                                     if let Some(ap) = item_map.get("additionalProperties") {
+                                        passthrough.remove("additionalProperties");
                                         merge_additional_properties_constraint(
                                             &mut merged_additional_properties,
                                             ap,
                                         );
                                     }
+                                    passthrough.remove("$id");
+                                    passthrough.remove("$schema");
+                                    if !passthrough.is_empty() {
+                                        resolved_all_of.push(Value::Object(passthrough));
+                                    }
                                 }
                             }
@@
                         if let Some(ap) = merged_additional_properties {
                             merged_schema.insert("additionalProperties".to_owned(), ap);
                         }
+                        if !resolved_all_of.is_empty() {
+                            merged_schema.insert("allOf".to_owned(), Value::Array(resolved_all_of));
+                        }
                         if !merged_required.is_empty() {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gts/src/store.rs` around lines 543 - 611, The code at the return statement
(line 611) creates and returns a merged schema but discards the resolved_all_of
items that contain unresolved $ref branches and non-mergeable constraints. To
fix this, after creating the merged_schema map and before returning it, check if
resolved_all_of is not empty. If it contains items, add an allOf field to
merged_schema that includes both the merged schema itself and any items from
resolved_all_of that couldn't be flattened. This ensures unresolved references
and non-mergeable constraints are preserved in the output schema rather than
being silently dropped.
gts/src/schema_traits.rs (1)

264-272: ⚠️ Potential issue | 🟠 Major

Pass the effective dialect to trait schema integrity validation.

validate_trait_schema_integrity() at line 101 validates individual trait schema fragments using jsonschema::validator_for(ts), which defaults to the crate's default draft (Draft 2020-12) per line 234. The dialect pinning at lines 301-305 happens afterward, so trait fragments that use dialect-specific keywords could pass validation under the wrong draft or fail unnecessarily under the correct one. Since EffectiveTraits holds the dialect-pinned schema, pass the dialect to the integrity check, or temporarily inject $schema into object fragments that don't already declare one.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gts/src/schema_traits.rs` around lines 264 - 272, The validation of trait
schema fragments in the loop over resolved_trait_schemas uses
jsonschema::validator_for(ts) which defaults to Draft 2020-12 instead of the
effective dialect that is pinned later. Update the validation logic to either
accept and pass the effective dialect to jsonschema::validator_for() so trait
fragments are validated against the correct draft, or temporarily inject a
`$schema` keyword into Value::Object fragments that do not already declare one
before calling the validator, ensuring fragments are validated against the
intended dialect.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gts/src/entities.rs`:
- Around line 239-244: The code in the GtsId parsing block accepts any valid
GtsId through try_new, but for schema entities the $id field should specifically
be a type ID (ending with ~), not an instance-like ID. Add an additional
validation check after GtsId::try_new succeeds to verify that the normalized
value is specifically a type ID by confirming it ends with the type ID suffix.
Only set gts_id, instance_id, and selected_entity_field if both the
GtsId::try_new validation and the type ID suffix validation pass.

In `@gts/src/ops.rs`:
- Around line 704-713: The validation loop for trait schemas currently uses an
if-let pattern that silently skips non-object schemas like booleans, causing
them to incorrectly pass validation. In the loop iterating through
traits.resolved_trait_schemas, modify the condition to explicitly reject
non-object schemas in addition to object schemas without additionalProperties:
false. Only object schemas with additionalProperties: false should be considered
valid; if the schema is not an object (ts.as_object() returns None), or if it's
an object without the required additionalProperties setting, an error should be
returned. Consider using an else clause to handle the non-object case or
restructure the logic to check for closed object schemas in a single validation.
- Around line 350-355: The validate_gts_keywords check is currently executed
after self.store.register has already mutated the store, which allows malformed
schemas to be persisted if validation fails. Move the entire if block containing
the validate_gts_keywords call to execute before self.store.register is invoked.
This ensures that if the GTS keywords validation fails in validate_gts_keywords,
the entity is rejected before any store mutations occur, preventing malformed
schemas from being left in the store.

In `@gts/src/schema_refs.rs`:
- Around line 53-67: The collect_gts_refs function recursively processes all
values in a schema object map without considering which keys they belong to,
causing it to incorrectly extract `$ref` from data-valued keywords like const,
default, examples, and enum. Modify the loop that iterates over map.values() to
skip values whose keys are known data-valued schema keywords. Instead of
iterating directly over map.values(), iterate over map.iter() and check if the
key is a data-valued keyword before recursing; if it is a data-valued keyword,
skip the recursive call for that value.

In `@gts/src/schema_traits.rs`:
- Around line 676-691: In the nested object handling branch of
materialize_traits_recursive, the code is replacing explicitly set but
type-mismatched values with newly materialized objects, which prevents JSON
Schema validation from catching the original error. Instead of unconditionally
replacing the result value with the materialized nested object, check whether
the original input value at that key exists and is not null before replacing it.
If an explicit value is already present in the input (even if it has the wrong
type), preserve it unchanged so that JSON Schema validation can detect and
report the type mismatch later.

In `@gts/src/store.rs`:
- Around line 474-490: The current merging logic in the
resolve_schema_refs_inner function when handling Value::Object(resolved_map)
performs a last-wins merge that can lose schema constraints like required fields
or additionalProperties restrictions from the referenced schema. Instead of
directly merging the resolved schema with sibling properties, detect when a $ref
has sibling properties and wrap them in an allOf array construct that contains
both the referenced schema and an object with the sibling properties. This
preserves constraints from both the referenced schema and the siblings rather
than allowing siblings to overwrite them. Update the code where merged is
created and returned to implement this allOf wrapping strategy.

In `@gts/src/x_gts_ref.rs`:
- Around line 416-424: The resolve_pointer method in the code that handles
x-gts-ref pointer resolution lacks cycle detection, which can cause infinite
recursion and stack overflow when circular references exist in the schema.
Create a new method resolve_pointer_checked that accepts an additional parameter
for tracking visited pointers using a BTreeSet, or modify the existing
resolve_pointer to include cycle detection. Before each pointer traversal, check
if the pointer string is already in the visited set; if it is, return None to
break the cycle. If not, insert it into the visited set and proceed with the
existing traversal logic. Ensure that any recursive calls to resolve pointers
during x-gts-ref hops also use the cycle-checking variant to maintain
consistency throughout the resolution chain.

---

Outside diff comments:
In `@gts/src/schema_traits.rs`:
- Around line 264-272: The validation of trait schema fragments in the loop over
resolved_trait_schemas uses jsonschema::validator_for(ts) which defaults to
Draft 2020-12 instead of the effective dialect that is pinned later. Update the
validation logic to either accept and pass the effective dialect to
jsonschema::validator_for() so trait fragments are validated against the correct
draft, or temporarily inject a `$schema` keyword into Value::Object fragments
that do not already declare one before calling the validator, ensuring fragments
are validated against the intended dialect.

In `@gts/src/store.rs`:
- Around line 1199-1208: The XGtsRefValidator should validate against the
resolved schema to ensure x-gts-ref constraints inside referenced schemas are
checked, not the raw schema. Pass the resolved schema (the result of
try_resolve_schema_refs) to the XGtsRefValidator instead of the raw schema
parameter. This will make x-gts-ref validation consistent with JSON Schema
validation which already uses the resolved schema, and ensure that constraints
in referenced schemas are properly validated during registered instance
validation.
- Around line 543-611: The code at the return statement (line 611) creates and
returns a merged schema but discards the resolved_all_of items that contain
unresolved $ref branches and non-mergeable constraints. To fix this, after
creating the merged_schema map and before returning it, check if resolved_all_of
is not empty. If it contains items, add an allOf field to merged_schema that
includes both the merged schema itself and any items from resolved_all_of that
couldn't be flattened. This ensures unresolved references and non-mergeable
constraints are preserved in the output schema rather than being silently
dropped.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1c39a5e0-161d-45af-b632-0784969dd8bf

📥 Commits

Reviewing files that changed from the base of the PR and between fbb8b1c and 604982d.

📒 Files selected for processing (13)
  • gts-id/src/gts_id.rs
  • gts-macros/tests/inheritance_tests.rs
  • gts-macros/tests/integration_tests.rs
  • gts/src/entities.rs
  • gts/src/lib.rs
  • gts/src/ops.rs
  • gts/src/schema_compat.rs
  • gts/src/schema_modifiers.rs
  • gts/src/schema_refs.rs
  • gts/src/schema_traits.rs
  • gts/src/store.rs
  • gts/src/store_test.rs
  • gts/src/x_gts_ref.rs

Comment thread gts/src/entities.rs
Comment thread gts/src/ops.rs Outdated
Comment thread gts/src/ops.rs Outdated
Comment thread gts/src/schema_refs.rs
Comment thread gts/src/schema_traits.rs Outdated
Comment thread gts/src/store.rs Outdated
Comment thread gts/src/x_gts_ref.rs
…lpers

API & store:
- Split GtsStore::new(Option<reader>) into new() and with_reader(); add
  Default impl; update all call sites and tests to GtsStore::new()
- Add validate_schema(type_id) -> ResolvedType, a self-contained resolved
  view (inlined schema, chain-merged effective traits, composed traits
  schema); export ResolvedType
- validate_payload/validate_instance reuse the resolved view instead of
  re-deriving traits per call
- try_resolve_schema_refs returns StoreError::CircularRef / UnresolvedRefs;
  resolve_schema_refs keeps unresolved refs in lenient output

Schema helpers:
- New schema_refs module: extract_gts_refs + ExtractRefsError for
  discovering schema dependencies before resolution
- schema_traits: extract inline_local_pointers (resolve JSON Pointer refs
  against the host doc); build_effective_traits / build_effective_traits_schema
  build the effective traits schema once
- schema_compat: add merge_additional_properties_constraint, a closedness-
  preserving lattice so a permissive allOf overlay can never loosen a closed
  base; apply it in extract_effective_schema and refine compatibility comments
- schema_modifiers: add validate_gts_keywords, a single structural gate for
  x-gts-final/abstract/traits keyword format + placement

Fixes:
- x_gts_ref: a relative x-gts-ref resolving to a wildcard pattern is now
  accepted via GtsIdPattern, matching the absolute branch
- entities: chained Type Schema doc fix (type_id is parent, None for
  standalone) plus coverage
- gts-id: reject the gts: form and assert is_valid agrees on gts://

Signed-off-by: Aviator 5 <ai.agent.tor@gmail.com>
…tion

- entities: require schema $id to be a type id (reject instance-like $id)
- ops: run validate_gts_keywords before register; reject boolean trait
  schemas as not closed
- schema_refs: skip data-valued keywords (const/default/examples/enum)
  when extracting $ref dependency edges
- schema_traits: don't materialize over a non-object value so type
  errors still reach validation
- store: combine $ref siblings via allOf instead of last-wins merge
- x_gts_ref: depth-guard resolve_pointer against cyclic x-gts-ref

Signed-off-by: Aviator 5 <ai.agent.tor@gmail.com>
…t trait checks

- Extract `$ref` resolution into a `schema_resolve` module built around a
  narrow `SchemaProvider` trait + `SchemaResolver`; `GtsStore` keeps thin
  `resolve_schema_refs`/`try_resolve_schema_refs` wrappers and implements the
  provider.
- Resolve `allOf`/`anyOf`/`oneOf` faithfully: inline `$ref`s in place and
  preserve the combinator (no lossy property flatten). Strip only root-only
  keys from inlined targets (`$id`/`$schema` and the `x-gts-*` type-level
  modifiers).
- `ResolvedType` gains `id` (GtsTypeId), `is_abstract`, `is_final`.
- `validate_schema`: abstract types now type-check the trait values they
  provide, deferring only the required-trait completeness check.
- Entity closedness check detects `additionalProperties: false` through `allOf`.
- Tests: cover 2–4 level trait merge (default/const/null), trait-schema
  `allOf`+`$ref` resolution, and exact `ResolvedType` assertions; move resolver
  unit tests into `schema_resolve_test` (driven via a mock provider) and keep
  wrapper smoke tests in `store_test`.

Signed-off-by: Aviator 5 <ai.agent.tor@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants