@@ -182,6 +182,9 @@ def _create_ref_resolver(self, schema: Dict[str, Any]) -> RefResolver:
182182
183183 def resolve_gts_ref (uri : str ) -> Dict [str , Any ]:
184184 """Resolve a GTS ID reference to its schema content."""
185+ # Issue #32: handle gts:// prefix
186+ if uri .startswith ("gts://" ):
187+ uri = uri [6 :]
185188 try :
186189 return self .get_schema_content (uri )
187190 except KeyError :
@@ -194,15 +197,69 @@ def resolve_gts_ref(uri: str) -> Dict[str, Any]:
194197 store [entity_id ] = entity .content
195198
196199 # Create RefResolver with custom handlers
197- resolver = RefResolver . from_schema (
198- schema , store = store , handlers = { " " : resolve_gts_ref }
199- )
200+ # Issue #32: Support "gts" scheme
201+ handlers = { "" : resolve_gts_ref , "gts " : resolve_gts_ref }
202+ resolver = RefResolver . from_schema ( schema , store = store , handlers = handlers )
200203 return resolver
201204
202205 def items (self ):
203206 """Return all entity ID and entity pairs."""
204207 return self ._by_id .items ()
205208
209+ @staticmethod
210+ def _validate_schema_refs (schema : Dict [str , Any ], path : str = "" ) -> None :
211+ """
212+ Validate all $ref values in a schema.
213+
214+ Rules:
215+ - Local refs (starting with #) are always valid
216+ - External refs MUST use gts:// URI format
217+ - The GTS ID after gts:// must be a valid GTS identifier
218+
219+ Args:
220+ schema: Schema content to validate
221+ path: Current path in schema (for error messages)
222+
223+ Raises:
224+ ValueError: If any $ref is invalid
225+ """
226+ if isinstance (schema , dict ):
227+ # Check $ref if present
228+ if "$ref" in schema :
229+ ref_uri = schema ["$ref" ]
230+ if isinstance (ref_uri , str ):
231+ current_path = f"{ path } .$ref" if path else "$ref"
232+
233+ # Local refs (JSON Pointer) are always valid
234+ if ref_uri .startswith ("#" ):
235+ pass # Valid local ref
236+ # GTS refs must use gts:// URI format
237+ elif ref_uri .startswith ("gts://" ):
238+ gts_id = ref_uri [6 :] # Strip prefix
239+ # Validate the GTS ID
240+ if not GtsID .is_valid (gts_id ):
241+ raise ValueError (
242+ f"Invalid $ref at '{ current_path } ': '{ ref_uri } ' contains invalid GTS identifier '{ gts_id } '"
243+ )
244+ # Any other external ref is invalid
245+ else :
246+ raise ValueError (
247+ f"Invalid $ref at '{ current_path } ': '{ ref_uri } ' must be a local ref (starting with '#') "
248+ f"or a GTS URI (starting with 'gts://')"
249+ )
250+
251+ # Recursively validate nested objects
252+ for key , value in schema .items ():
253+ if key == "$ref" :
254+ continue # Already validated above
255+ nested_path = f"{ path } .{ key } " if path else key
256+ GtsStore ._validate_schema_refs (value , nested_path )
257+
258+ elif isinstance (schema , list ):
259+ for idx , item in enumerate (schema ):
260+ nested_path = f"{ path } [{ idx } ]"
261+ GtsStore ._validate_schema_refs (item , nested_path )
262+
206263 def _validate_schema_x_gts_refs (self , gts_id : str ) -> None :
207264 """
208265 Validate a schema's x-gts-ref fields.
@@ -256,15 +313,30 @@ def validate_schema(self, gts_id: str) -> None:
256313 if not isinstance (schema_content , dict ):
257314 raise ValueError (f"Schema '{ gts_id } ' content must be a dictionary" )
258315
316+ # Issue #25: strict check, no GTS IDs in $schema
317+ meta_schema_url = schema_content .get ("$schema" )
318+ if meta_schema_url and isinstance (meta_schema_url , str ):
319+ if meta_schema_url .startswith ("gts." ) or meta_schema_url .startswith (
320+ "gts://"
321+ ):
322+ raise ValueError (
323+ f"Invalid $schema URL '{ meta_schema_url } ': must be a standard JSON Schema URL, not a GTS ID"
324+ )
325+
259326 logging .info (f"Validating schema { gts_id } " )
260327
261- # 1. Validate against JSON Schema meta-schema
328+ # 1. Validate $ref fields - must be local (#...) or gts:// URIs
329+ # Issue #32: This validation must happen first to enforce strict $ref format
330+ self ._validate_schema_refs (schema_content , "" )
331+
332+ # 2. Validate x-gts-ref fields (before JSON Schema validation)
333+ self ._validate_schema_x_gts_refs (gts_id )
334+
335+ # 3. Validate against JSON Schema meta-schema
262336 try :
263337 from jsonschema import Draft7Validator
264338 from jsonschema .validators import validator_for
265339
266- # Determine which meta-schema to use based on $schema field
267- meta_schema_url = schema_content .get ("$schema" )
268340 if meta_schema_url :
269341 # Use the appropriate validator for the schema version
270342 validator_class = validator_for ({"$schema" : meta_schema_url })
@@ -277,9 +349,6 @@ def validate_schema(self, gts_id: str) -> None:
277349 except Exception as e :
278350 raise Exception (f"JSON Schema validation failed for '{ gts_id } ': { str (e )} " )
279351
280- # 2. Validate x-gts-ref fields
281- self ._validate_schema_x_gts_refs (gts_id )
282-
283352 def validate_instance (
284353 self ,
285354 gts_id : str ,
0 commit comments