Skip to content

feat: implement OP#12 schema-vs-schema validation#11

Merged
Artifizer merged 8 commits intoGlobalTypeSystem:mainfrom
GeraBart:fix/oneOf_fix
Feb 17, 2026
Merged

feat: implement OP#12 schema-vs-schema validation#11
Artifizer merged 8 commits intoGlobalTypeSystem:mainfrom
GeraBart:fix/oneOf_fix

Conversation

@GeraBart
Copy link
Copy Markdown
Contributor

@GeraBart GeraBart commented Feb 17, 2026

fix of #10

Summary by CodeRabbit

  • New Features

    • Schema compatibility validation against parent schemas and two new API endpoints for validating schemas and entities.
  • Bug Fixes

    • Improved schema normalization and combinator evaluation to reduce false errors and better report conflicts.
  • Documentation

    • README, examples, and docs updated to reference the new validation endpoints and usage.
  • Chores

    • Project version bumped to 0.2.0.
  • Tests

    • Added clarifying test annotations about combinator coverage.

Signed-off-by: GeraBart <246844849+GeraBart@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 17, 2026

No actionable comments were generated in the recent review. 🎉


📝 Walkthrough

Walkthrough

Adds GtsStore.validateSchemaAgainstParent with deep schema resolution and constraint comparison, normalizes/prunes x-gts-ref and combinators, expands x-gts-ref combinator traversal/evaluation (containsXGtsRef, duplicate implementation present), exposes POST /validate-schema and POST /validate-entity endpoints, and bumps package version to 0.2.0.

Changes

Cohort / File(s) Summary
Version
package.json
Bump version from 0.1.1 to 0.2.0; upgraded fastify to v5 and removed fastify-cors.
Store / Schema validation
src/store.ts
Add ResolvedSchema and GtsStore.validateSchemaAgainstParent(schemaId); implement full schema resolution/flattening, overlay extraction, property-constraint comparison, merge helpers, recursive normalization (strip x-gts-ref) and pruning of empty combinators; many private helpers added.
x-gts-ref logic
src/x-gts-ref.ts
Recursive traversal of allOf; restrict anyOf/oneOf evaluation to branches containing x-gts-ref via containsXGtsRef; propagate combinator errors and detect oneOf conflicts. Note: duplicate private containsXGtsRef implementations present (merge artifact).
Server API & types
src/server/server.ts, src/server/types.ts
Add POST /validate-schema and POST /validate-entity endpoints and request body types ValidateSchemaBody and ValidateEntityBody; handlers call validateSchemaAgainstParent or validateInstance and return structured validation results.
Tests & Submodule
tests/gts.test.ts, .gts-spec
Add two non-functional test comments; update .gts-spec submodule reference commit.
Docs
README.md
Mark schema validation implemented (OP#12), add/adjust docs and examples, and document new validation endpoints; minor formatting edits.

Sequence Diagram

sequenceDiagram
    participant Client as rgba(70,130,180,0.5) Client
    participant Server as rgba(34,139,34,0.5) Server
    participant Store as rgba(255,165,0,0.5) GtsStore
    participant XG as rgba(147,112,219,0.5) x-gts-ref
    participant DB as rgba(220,20,60,0.5) DB

    Client->>Server: POST /validate-schema { schema_id } or /validate-entity { entity_id | gts_id }
    activate Server
    Server->>DB: fetch entity by id
    DB-->>Server: entity (schema or instance)
    alt entity is schema
        Server->>Store: validateSchemaAgainstParent(schema_id)
        activate Store
        Store->>Store: resolve & normalize schema (strip x-gts-ref, prune combinators)
        Store->>XG: evaluate combinators (allOf/anyOf/oneOf) using containsXGtsRef
        XG-->>Store: validation result / errors
        Store-->>Server: ValidationResult
        deactivate Store
    else entity is instance
        Server->>Store: validateInstance(entity)
        Store-->>Server: ValidationResult
    end
    Server-->>Client: return ValidationResult or structured error
    deactivate Server
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Poem

🐰 I hopped through schemas, nibbled each ref,
I pruned empty branches and smoothed every comb,
I matched overlays to parents with care,
oneOf/anyOf/allOf now sing in my home,
Tiny hops, tidy types — a happy schema roam 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: implement OP#12 schema-vs-schema validation' directly and clearly summarizes the main change—adding schema validation functionality as part of OP#12. It is concise, specific, and accurately reflects the primary objective of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

@GeraBart GeraBart linked an issue Feb 17, 2026 that may be closed by this pull request
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/store.ts`:
- Around line 227-237: The current combinator cleanup indiscriminately removes
all empty object subschemas from normalized.oneOf/anyOf/allOf; instead, only
remove subschemas that became empty due to stripping x-gts-ref. When you strip
x-gts-ref earlier, mark those subschemas (e.g., add a non-serializing sentinel
property like __gtsStrippedRef = true or store their path in a Set). Then update
this combinator loop to filter out only empty objects that also carry that
sentinel (or are present in the Set), leaving intentionally empty {} branches
intact; finally ensure you remove the sentinel/Set entries after cleanup so no
runtime artifacts remain.

In `@src/x-gts-ref.ts`:
- Around line 85-133: The anyOf/oneOf handling currently runs x-gts-ref checks
across only the ref-containing branches and rejects if none pass even when a
non-x-gts-ref branch actually validated; change the logic to first determine
which anyOf/oneOf branches actually validate the instance (e.g. call the schema
validator used elsewhere or add an isValidAgainstSchema(instance, subSchema,
rootSchema) helper), then only run containsXGtsRef + visitInstance on those
branches that both validate and contain x-gts-ref; push errors from
branchResults only when the branch validated but its x-gts-ref checks failed,
and for oneOf ensure you count only validated branches when computing
passingCount (refer to containsXGtsRef, visitInstance, branchResults, and
errors).

Comment thread src/store.ts Outdated
Comment thread src/x-gts-ref.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/x-gts-ref.ts`:
- Around line 85-133: The anyOf/oneOf logic currently treats a branch as
"passing" based solely on x-gts-ref errors collected by visitInstance, so a
branch that fails general JSON Schema constraints can still be counted as
passing; fix by first performing a full schema validation of each branch and
only run visitInstance / consider x-gts-ref results for branches that actually
validate. Concretely, for each subSchema in schema.anyOf/schema.oneOf (and after
filtering with containsXGtsRef), run the existing full-schema validator (e.g.,
this.ajv.validate or the project’s validateAgainstSchema method) against
instance; if the branch does not validate, ignore it for counting and do not
treat its x-gts-ref errors as a pass (you may still optionally collect x-gts-ref
errors only for branches that validate); if it does validate, then call
this.visitInstance(instance, subSchema, path, rootSchema, branchErrors) and use
branchErrors.length === 0 to determine passing for anyOf/oneOf logic (adjust the
anyPassed and passingCount calculations accordingly).

Signed-off-by: GeraBart <246844849+GeraBart@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/gts.test.ts (1)

417-810: Add a mixed-branch test to lock intended semantics.

Current coverage is strong, but there’s no case where a non‑x-gts-ref branch validates while all x-gts-ref branches fail. Adding such a test would clarify the expected behavior for mixed combinators and protect future changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/gts.test.ts` around lines 417 - 810, Add a new test under the "oneOf
with mixed x-gts-ref and plain branches" suite that asserts a non-x-gts-ref
branch can validate even when all x-gts-ref branches fail: create a schema (use
the existing $$id 'gts.test.pkg.ns.mixed_oneof.v1~' pattern) with oneOf: [{
'x-gts-ref': 'gts.test.pkg.ns.target_a.*' }, { minLength: 1 }], instantiate a
GTS with new GTS({ validateRefs: false }), register only a plain string
target/instance (but do NOT register the target_a entries so the x-gts-ref
branch fails), register the schema and an instance whose ref meets minLength but
does not match any x-gts-ref pattern, call g.validateInstance(instance.gtsId)
and expect res.ok toBe(true); reference GTS, g.register, and g.validateInstance
to locate where to add the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/x-gts-ref.ts`:
- Around line 85-133: The anyOf/oneOf handling currently only inspects branches
with containsXGtsRef and treats a branch as "passing" based solely on
visitInstance (which doesn't run full structural validation), causing valid
instances that match a non-x-gts-ref branch to be rejected; change the logic to
first determine which anyOf/oneOf branches structurally validate the instance
(e.g., using the schema validator you already use elsewhere or a call that
performs full Ajv validation), then for branches that both validate structurally
and contain x-gts-ref run visitInstance to collect XGtsRefValidationError
results, and finally aggregate errors only if no structurally-valid branch
passes the x-gts-ref checks (for anyOf) or if zero or >1 structurally-valid
branches pass the x-gts-ref checks (for oneOf), updating the code paths that
reference visitInstance, containsXGtsRef, and XGtsRefValidationError to reflect
this two-step (structural then x-gts-ref) validation and error aggregation.

---

Nitpick comments:
In `@tests/gts.test.ts`:
- Around line 417-810: Add a new test under the "oneOf with mixed x-gts-ref and
plain branches" suite that asserts a non-x-gts-ref branch can validate even when
all x-gts-ref branches fail: create a schema (use the existing $$id
'gts.test.pkg.ns.mixed_oneof.v1~' pattern) with oneOf: [{ 'x-gts-ref':
'gts.test.pkg.ns.target_a.*' }, { minLength: 1 }], instantiate a GTS with new
GTS({ validateRefs: false }), register only a plain string target/instance (but
do NOT register the target_a entries so the x-gts-ref branch fails), register
the schema and an instance whose ref meets minLength but does not match any
x-gts-ref pattern, call g.validateInstance(instance.gtsId) and expect res.ok
toBe(true); reference GTS, g.register, and g.validateInstance to locate where to
add the test.

Signed-off-by: GeraBart <246844849+GeraBart@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
tests/gts.test.ts (2)

458-475: Consider extracting repeated fixture registration into a helper.

The registration block (lines 459-464) is repeated verbatim in many tests within this suite. While the current approach ensures test isolation, a helper function would reduce duplication without sacrificing clarity.

💡 Suggested helper approach
// Add at the top of the describe block
function setupGtsWithFixtures(): GTS {
  const g = new GTS({ validateRefs: false });
  g.register(refTargetA);
  g.register(refTargetB);
  g.register(instanceA);
  g.register(instanceB);
  return g;
}

// Then in tests:
const g = setupGtsWithFixtures();
g.register(schema);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/gts.test.ts` around lines 458 - 475, Extract the repeated registration
block into a helper function (e.g., setupGtsWithFixtures) defined at the top of
the describe block that creates new GTS({ validateRefs: false }), registers
refTargetA, refTargetB, instanceA, and instanceB, and returns the GTS instance;
then update tests like the one using GTS and schema to call this helper (const g
= setupGtsWithFixtures()) and only register test-specific items such as schema
and inst, ensuring each test still creates its own GTS instance for isolation
while removing duplication.

779-808: Clarify the non-standard oneOf semantics in comments.

This test documents that non-x-gts-ref branches are excluded from oneOf counting. In standard JSON Schema, the value would match both branches (x-gts-ref pattern and minLength: 1), causing oneOf to fail. Consider adding a comment explaining this deliberate departure from standard semantics for future maintainers.

📝 Suggested comment addition
     describe('oneOf with mixed x-gts-ref and plain branches', () => {
+      // NOTE: When evaluating oneOf with x-gts-ref branches, only branches containing
+      // x-gts-ref are counted for the "exactly one" requirement. Plain JSON Schema
+      // branches are validated separately and don't contribute to the oneOf count.
       test('non-x-gts-ref branches do not interfere with counting', () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/gts.test.ts` around lines 779 - 808, Add a brief comment above this
test (or directly above the schema variable) explaining that GTS intentionally
treats oneOf differently by excluding non-x-gts-ref branches from the oneOf
match count, so the pattern branch ('x-gts-ref') is evaluated independently of
the plain JSON Schema branch (e.g., { minLength: 1 }) and thus the instance is
considered a single match; reference the test's schema variable and the GTS
class/validateInstance flow so future maintainers know this is a deliberate,
non-standard deviation from JSON Schema semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/gts.test.ts`:
- Around line 458-475: Extract the repeated registration block into a helper
function (e.g., setupGtsWithFixtures) defined at the top of the describe block
that creates new GTS({ validateRefs: false }), registers refTargetA, refTargetB,
instanceA, and instanceB, and returns the GTS instance; then update tests like
the one using GTS and schema to call this helper (const g =
setupGtsWithFixtures()) and only register test-specific items such as schema and
inst, ensuring each test still creates its own GTS instance for isolation while
removing duplication.
- Around line 779-808: Add a brief comment above this test (or directly above
the schema variable) explaining that GTS intentionally treats oneOf differently
by excluding non-x-gts-ref branches from the oneOf match count, so the pattern
branch ('x-gts-ref') is evaluated independently of the plain JSON Schema branch
(e.g., { minLength: 1 }) and thus the instance is considered a single match;
reference the test's schema variable and the GTS class/validateInstance flow so
future maintainers know this is a deliberate, non-standard deviation from JSON
Schema semantics.

@GeraBart GeraBart marked this pull request as draft February 17, 2026 04:01
@GeraBart GeraBart marked this pull request as ready for review February 17, 2026 11:55
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/store.ts (1)

1317-1331: Redundant nested condition check.

The nested condition on lines 1321-1326 is redundant. When overlay.additionalProperties === undefined is true (line 1319), the inner check on line 1324 will always be true.

♻️ Proposed simplification
     // Check additionalProperties
     if (baseResolved.additionalProperties === false) {
       if (overlay.additionalProperties === undefined) {
-        // Derived omits AP when base has false → fail (loosening)
-        if (Object.keys(overlayProps).length > 0 || overlay.additionalProperties === undefined) {
-          // Only fail if the overlay is actually present as a separate subschema
-          // Check if overlay explicitly omitted it
-          if (overlay.additionalProperties === undefined) {
-            errors.push('Base has additionalProperties: false but derived does not restate it');
-          }
-        }
+        // Derived omits additionalProperties when base has false → fail (loosening)
+        errors.push('Base has additionalProperties: false but derived does not restate it');
       } else if (overlay.additionalProperties === true) {
         errors.push('Cannot loosen additionalProperties from false to true');
       }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/store.ts` around lines 1317 - 1331, The nested redundant check is that
inside the block where overlay.additionalProperties === undefined you re-check
overlay.additionalProperties === undefined again; simplify by removing the inner
conditional and directly push the error when overlay.additionalProperties is
undefined and overlayProps is non-empty: i.e., in the branch that tests
baseResolved.additionalProperties === false and overlay.additionalProperties ===
undefined, only test Object.keys(overlayProps).length > 0 and then push the
message into errors (leave the separate else-if overlay.additionalProperties ===
true branch as-is); reference variables: baseResolved, overlay, overlayProps,
errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Around line 35-36: The markdown warning is caused by underscores in the
filename examples being parsed as emphasis; edit the README entries under "YAML
support" and "TypeSpec support" and wrap the filenames in inline code or escape
the underscores/asterisk so they are literal (e.g., replace "_.yml, _.yaml" with
`_.yml`, `_.yaml` and replace "*.tsp" or "\*.tsp" with `*.tsp`), making sure the
checklist lines "YAML support" and "TypeSpec support" show the filenames as code
literals.

In `@src/server/server.ts`:
- Around line 595-602: handleValidateSchema is missing input validation for
schema_id; add a runtime check in the handleValidateSchema method (the handler
for ValidateSchemaBody) to ensure request.body.schema_id is present and
non-empty before calling
this.store['store'].validateSchemaAgainstParent(schema_id). If schema_id is
missing or invalid, return a 400 Bad Request (or throw a Fastify error) with a
clear message such as "Missing required field: schema_id" so callers get a
proper validation error instead of "Entity not found: undefined".

---

Nitpick comments:
In `@src/store.ts`:
- Around line 1317-1331: The nested redundant check is that inside the block
where overlay.additionalProperties === undefined you re-check
overlay.additionalProperties === undefined again; simplify by removing the inner
conditional and directly push the error when overlay.additionalProperties is
undefined and overlayProps is non-empty: i.e., in the branch that tests
baseResolved.additionalProperties === false and overlay.additionalProperties ===
undefined, only test Object.keys(overlayProps).length > 0 and then push the
message into errors (leave the separate else-if overlay.additionalProperties ===
true branch as-is); reference variables: baseResolved, overlay, overlayProps,
errors.

Comment thread README.md Outdated
Comment thread src/server/server.ts
Comment thread src/store.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/server/types.ts (1)

76-83: Model “one-of” input to prevent {} bodies.

ValidateEntityBody currently allows an empty object, which undermines the intended contract and shifts all validation to runtime. Consider a union (or XOR) type to enforce at least one ID at compile time.

♻️ Suggested type shape
-export interface ValidateEntityBody {
-  entity_id?: string;
-  gts_id?: string;
-}
+export type ValidateEntityBody =
+  | { entity_id: string; gts_id?: never }
+  | { gts_id: string; entity_id?: never };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/types.ts` around lines 76 - 83, ValidateEntityBody currently
permits an empty object; replace the loose interface with a discriminated union
so callers must provide exactly one identifier at compile time (e.g., a union of
a shape with entity_id present and gts_id absent vs. a shape with gts_id present
and entity_id absent). Update the exported type name ValidateEntityBody to this
union (or use a reusable XOR/OneOf utility type) and ensure any code referencing
ValidateEntityBody (validators, handlers) compiles against the new stricter
type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/store.ts`:
- Around line 1103-1112: The code calls resolveSchemaFully(parentEntity.content)
without verifying parentEntity is actually a schema or has content; update the
parent resolution to validate parentEntity.type (e.g., === 'schema' or the
project's canonical schema discriminator) and that parentEntity.content is
present before calling resolveSchemaFully, and if the check fails return the
existing error shape (e.g., { id: schemaId, ok: false, error: ... }); use
symbols parentRef, parentId, parentEntity, this.get(...), parentEntity.content,
resolveSchemaFully and schemaId to locate and implement the guard.
- Around line 1321-1334: The current additionalProperties check incorrectly
requires derived schemas to restate additionalProperties: false; update the
validation in the additionalProperties block so that when
baseResolved.additionalProperties === false you only error if
overlay.additionalProperties === true (i.e. the overlay explicitly loosens the
constraint), and do not push an error when overlay.additionalProperties is
undefined or overlayProps exist; modify the logic around baseResolved, overlay,
overlayProps and errors to remove the “must restate false” condition and only
report "Cannot loosen additionalProperties from false to true" when
overlay.additionalProperties === true.
- Around line 1250-1272: The current use of Object.assign on overlay.properties
(inside the schema.allOf loop and the top-level properties merge) overwrites
earlier property constraints when multiple subschemas touch the same property;
instead iterate each property key and merge per-property objects: for each key
in sub.properties (and for schema.properties) if overlay.properties[key] exists
and both values are objects, shallow-merge fields into overlay.properties[key]
(e.g., Object.assign(overlay.properties[key], subProp)), otherwise set
overlay.properties[key] = subProp; keep the existing handling for
overlay.required and overlay.additionalProperties unchanged.

---

Nitpick comments:
In `@src/server/types.ts`:
- Around line 76-83: ValidateEntityBody currently permits an empty object;
replace the loose interface with a discriminated union so callers must provide
exactly one identifier at compile time (e.g., a union of a shape with entity_id
present and gts_id absent vs. a shape with gts_id present and entity_id absent).
Update the exported type name ValidateEntityBody to this union (or use a
reusable XOR/OneOf utility type) and ensure any code referencing
ValidateEntityBody (validators, handlers) compiles against the new stricter
type.

Comment thread src/store.ts
Comment thread src/store.ts
Comment thread src/store.ts
@GeraBart GeraBart changed the title Fix/one of fix feat: implement OP#12 schema-vs-schema validation Feb 17, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/store.ts (1)

1379-1409: Consider the impact of hasNewConstraints allowing constraint drops.

The logic at lines 1396-1404 allows dropping base constraints (e.g., maxLength) if the derived schema adds any new constraint keyword. This means a derived schema adding minItems could effectively "unlock" dropping maxLength without an error.

If this is intentional for allOf semantics (base constraints remain in effect anyway), consider adding a clarifying comment. Otherwise, this could allow loosening validation in unexpected ways.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/store.ts` around lines 1379 - 1409, The current logic uses
hasNewConstraints (computed from CONSTRAINT_KEYWORDS, baseConstraintKeys,
derivedConstraintKeys) to allow dropping base constraints like maxLength if any
other new constraint exists in the derived schema; if that is not intentional,
change the check in the max-constraints loop (the branch that currently does "if
(derived[kw] === undefined) { if (!hasNewConstraints) { errors.push(...)" ) to
always push an error for dropped constraints (remove the hasNewConstraints
gating) so any missing base[kw] in derived triggers errors.push(`Property
'${propPath}' drops constraint '${kw}'`); alternatively, if the original
behavior is intentional, add a clarifying comment above
CONSTRAINT_KEYWORDS/hasNewConstraints explaining why any new constraint in the
derived schema is allowed to permit dropping other base constraints.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/store.ts`:
- Around line 1332-1339: The current check incorrectly treats a missing
overlay.additionalProperties as an error when baseResolved.additionalProperties
=== false; update the logic in the same validation block (referencing
baseResolved, overlay, additionalProperties and errors) to only push an error if
the derived/overlay explicitly sets additionalProperties === true while the
baseResolved has additionalProperties === false; do not push an error when
overlay.additionalProperties is undefined (inherited behavior), so remove or
change the branch that adds the "does not restate it" message.

---

Nitpick comments:
In `@src/store.ts`:
- Around line 1379-1409: The current logic uses hasNewConstraints (computed from
CONSTRAINT_KEYWORDS, baseConstraintKeys, derivedConstraintKeys) to allow
dropping base constraints like maxLength if any other new constraint exists in
the derived schema; if that is not intentional, change the check in the
max-constraints loop (the branch that currently does "if (derived[kw] ===
undefined) { if (!hasNewConstraints) { errors.push(...)" ) to always push an
error for dropped constraints (remove the hasNewConstraints gating) so any
missing base[kw] in derived triggers errors.push(`Property '${propPath}' drops
constraint '${kw}'`); alternatively, if the original behavior is intentional,
add a clarifying comment above CONSTRAINT_KEYWORDS/hasNewConstraints explaining
why any new constraint in the derived schema is allowed to permit dropping other
base constraints.

Signed-off-by: GeraBart <246844849+GeraBart@users.noreply.github.com>
Signed-off-by: GeraBart <246844849+GeraBart@users.noreply.github.com>
Signed-off-by: GeraBart <246844849+GeraBart@users.noreply.github.com>
Signed-off-by: GeraBart <246844849+GeraBart@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/store.ts`:
- Around line 1332-1338: The check in src/store.ts incorrectly treats
overlay.additionalProperties === undefined as an error when
baseResolved.additionalProperties === false; update the logic around
baseResolved.additionalProperties and overlay.additionalProperties (the block
that currently pushes "Base has additionalProperties: false but derived does not
restate it") so that you only error when the overlay explicitly loosens the
constraint (overlay.additionalProperties === true) and do not error when
overlay.additionalProperties is undefined (inheritance case); keep the existing
error for the explicit true case and allow overlay to omit the property to
inherit the base behavior.

Signed-off-by: GeraBart <246844849+GeraBart@users.noreply.github.com>
@Artifizer Artifizer merged commit 12082da into GlobalTypeSystem:main Feb 17, 2026
8 checks passed
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.

XGtsRefValidator does not traverse oneOf/anyOf/allOf subschemas

2 participants