Skip to content

docs(adr): add ADR-0001 separating additionalProperties from x-gts-sealed#74

Draft
aviator5 wants to merge 1 commit into
GlobalTypeSystem:mainfrom
aviator5:additional-properties-adr
Draft

docs(adr): add ADR-0001 separating additionalProperties from x-gts-sealed#74
aviator5 wants to merge 1 commit into
GlobalTypeSystem:mainfrom
aviator5:additional-properties-adr

Conversation

@aviator5
Copy link
Copy Markdown
Collaborator

@aviator5 aviator5 commented May 20, 2026

  • Document three concrete problems with the current spec's overloaded use of additionalProperties: false — allOf-applicator footguns, forward-compatibility break, and missing middle ground between fully-open derivation and x-gts-final
  • Connect the closed content model to the tolerant reader principle (Postel's law), with pipeline-enrichment, vendor-extension, and producer-side-optionality scenarios beyond pure version evolution
  • Propose x-gts-sealed as a per-schema-object derivation-control modifier enforced by OP#12; demote additionalProperties: false to its native JSON Schema instance-validation role
  • Capture considered options with explicit per-option mapping of which problems they address and worked code demonstrations, plus impact on OP#6/OP#12 and reference implementations
  • Place the ADR at repo-root adr/ for visibility

Summary by CodeRabbit

  • Documentation
    • Introduced architecture decision documenting a new schema modifier for controlling property inheritance and closure behavior during schema derivation and registration. Includes specification updates, authoring guidance with examples, and identifies testing, implementation, and conformance impacts.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c8de9f2-2d31-4550-932c-db982f6a51d3

📥 Commits

Reviewing files that changed from the base of the PR and between 33631a0 and 7137c8d.

📒 Files selected for processing (1)
  • adr/0001-additional-properties-and-sealed.md

📝 Walkthrough

Walkthrough

Adds ADR-0001 introducing x-gts-sealed, a per-object schema modifier enforced during registration to prevent derived schemas from adding new property keys at sealed positions while leaving additionalProperties as the instance-validation construct.

Changes

x-gts-sealed Modifier ADR

Layer / File(s) Summary
Problem statement and rationale
adr/0001-additional-properties-and-sealed.md
Explains conflation between additionalProperties: false and derivation closedness, shows composition pitfalls and why prior approaches fail.
Design selection: x-gts-sealed
adr/0001-additional-properties-and-sealed.md
Chooses x-gts-sealed as a per-type: object modifier; clarifies additionalProperties remains for instance validation.
Normative spec updates & conformance
adr/0001-additional-properties-and-sealed.md
Forbids unsafe additionalProperties: false in composed schemas; defines x-gts-sealed semantics, OP#12 enforcement, test/reference-implementation impacts.
Worked examples and comparisons
adr/0001-additional-properties-and-sealed.md
Provides pros/cons and multiple examples, including mixed sealing where nested objects are sealed while the top-level remains open.
Related sections and open questions
adr/0001-additional-properties-and-sealed.md
References related spec topics and records follow-up questions (e.g., patternProperties, structured error codes).

"🐰 A schema's in a bind when properties run wild,
So sealed steps in, keeping types reconciled,
No new keys shall pass where the seal is applied,
But additionalProperties keeps instances tied,
Two concerns, now separate, each satisfied!"

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 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 change: adding ADR-0001 that separates the concerns of additionalProperties from a new x-gts-sealed schema modifier.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

@aviator5 aviator5 requested a review from Artifizer May 20, 2026 20:51
@aviator5
Copy link
Copy Markdown
Collaborator Author

@Artifizer please take a look

@aviator5 aviator5 force-pushed the additional-properties-adr branch from e63821b to 33631a0 Compare May 21, 2026 07:57
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

🤖 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 `@adr/0001-additional-properties-and-sealed.md`:
- Around line 281-282: The sentence claiming full backward compatibility
contradicts the new §5 MUST-NOT rule; update the paragraph to narrow the claim
(e.g., replace the current sentence with a line such as “No runtime
instance-validation behavior changes unless an implementation adopts the
x-gts-sealed extension” or equivalent) so it accurately reflects that
registration-time conformance may reject previously-accepted schemas while
runtime validation remains unchanged absent x-gts-sealed; reference §5 MUST-NOT
and the term x-gts-sealed when editing the sentence.
- Around line 232-233: The OP#12 enforcement text for "x-gts-sealed" currently
only references the "properties" key and leaves "patternProperties" ambiguous;
update the ADR text around OP#12 (the paragraphs discussing "OP#12 enforcement"
and the later lines about patternProperties) to either (a) explicitly include
patternProperties in the sealed check (e.g., state that keys matched by
patternProperties are treated as sealed and derived schemas may not introduce
new pattern-matching keys), or (b) explicitly exclude patternProperties from
OP#12 and note that pattern-based key semantics are deferred to a follow-up ADR;
reference the "x-gts-sealed" / "OP#12 enforcement" paragraphs and the later
"patternProperties" mention when making this clarification so implementations
have an unambiguous normative decision.
- Around line 220-223: Update the rule for `additionalProperties: false` in
adr/0001-additional-properties-and-sealed.md to narrow the disallowed cases:
forbid only when the set of allowed property names is introduced or becomes
known inside composition branches or `$ref` (i.e., when the object with
`additionalProperties: false` does not itself declare the complete set of
`properties`/`patternProperties`), allow `additionalProperties: false` when all
property names are declared at that same object level even if
`oneOf`/`anyOf`/`allOf` are used to constrain values, and add an explicit
recommendation to prefer `unevaluatedProperties: false` for modular/composed
schemas.
🪄 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: 03d841d3-30ed-47d1-b640-ad0d66dbbec7

📥 Commits

Reviewing files that changed from the base of the PR and between bd9f4cf and 33631a0.

📒 Files selected for processing (1)
  • adr/0001-additional-properties-and-sealed.md

Comment on lines +220 to +223
> `additionalProperties: false` MUST NOT appear in a schema object that also contains `allOf`, `$ref`, `oneOf`, or `anyOf` referencing another schema (whether at the top level of the schema, or inside an `allOf` branch with sibling property declarations). Such combinations are rejected by JSON Schema 2020-12 in-place-applicator semantics and would reject the very inherited properties the author intended to keep. `additionalProperties: false` IS allowed in:
> - schemas that do not compose via `allOf`/`$ref` (standalone or root base schemas with no GTS parent), and
> - inline-defined nested object schemas inside a derived schema (e.g., the `payload` sub-schema of an event derivation).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In JSON Schema draft 2020-12, does using additionalProperties: falsetogether withoneOforanyOfalways produce the same in-place-applicator inheritance issue asallOf+$ref, or are there valid patterns where it is safe?

💡 Result:

No—additionalProperties: false combined with oneOf/anyOf does not always force the same “in-place-applicator inheritance” problem you may have seen with allOf + $ref. In JSON Schema 2020-12, the root issue is that additionalProperties only accounts for property evaluations “from its immediate sibling keywords”, and it cannot “see through” applicators/composition keywords like allOf/anyOf/oneOf the way 2020-12’s unevaluatedProperties can. This is why patterns that work with unevaluatedProperties can fail with additionalProperties in composition/ref “inheritance” situations [1][2][3][4]. So there are safe patterns where additionalProperties: false behaves correctly with oneOf/anyOf, but they are constrained: 1) Safe pattern: every possible property name is declared at the same object level as additionalProperties If the object schema that contains additionalProperties: false also has a complete properties/patternProperties coverage for all allowed keys (without relying on additional keys being introduced/validated only inside oneOf/anyOf branches), then additionalProperties: false is safe because there are no “unknown” keys from the viewpoint of that immediate object schema [2][5]. 2) Safe pattern: avoid “branch-specific allowed keys” at that object level If each oneOf/anyOf branch constrains only the values of already-declared keys (i.e., keys are present in the root properties/patternProperties set), then additionalProperties: false will not suffer from missing coverage due to composition boundaries [2][5]. 3) Unsafe pattern (common cause): “inherit” or “introduce” allowed properties only inside oneOf/anyOf/allOf branches If you use composition to modularize object shapes (e.g., $ref + allOf or oneOf branches that each add their own properties) while keeping additionalProperties: false in a surrounding schema, you can get failures because additionalProperties does not see that those property names were validated inside the composition branches; this “inheritance”/modularity problem is a known behavior and unevaluatedProperties is proposed as the fix specifically because it can see through composition/applicators and $ref [3][4]. In short: - additionalProperties: false is not intrinsically tied to allOf vs oneOf/anyOf; the failure mode occurs when allowed property names are only “known” within composition branches but not by the immediate object-level properties/patternProperties that additionalProperties evaluates against [2][3][4]. - unevaluatedProperties: false is the 2020-12 construct designed to be safe with “inheritance”-style modular schemas using allOf/anyOf/oneOf and $ref, because it can see through those applicators [3][1]. If you want “strict object keys” across oneOf/anyOf/allOf branches with refs, prefer unevaluatedProperties: false (possibly combined with keeping properties/patternProperties for truly unconditional constraints), rather than relying on additionalProperties: false in a parent schema that expects branch-introduced properties to count as evaluated [1][3].

Citations:


Narrow the additionalProperties: false MUST-NOT scope for oneOf/anyOf

In JSON Schema 2020-12, the problematic behavior is that additionalProperties: false is evaluated only against the immediate object schema’s declared properties/patternProperties, and it does not “see through” composition/applicators and $ref the way unevaluatedProperties can. As a result, additionalProperties: false with oneOf/anyOf is not inherently unsafe; it’s safe when the set of allowed property names is fully declared at that same object level (and composition branches only constrain values of those already-declared keys).

Update the rule at adr/0001-additional-properties-and-sealed.md lines 220-223 to prohibit only the unsafe pattern where the allowed/validated property names are introduced or become “known” only inside oneOf/anyOf/allOf branches (or via $ref) rather than being declared in the object that contains additionalProperties: false—or explicitly recommend using unevaluatedProperties: false for modular/composed schemas.

🤖 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 `@adr/0001-additional-properties-and-sealed.md` around lines 220 - 223, Update
the rule for `additionalProperties: false` in
adr/0001-additional-properties-and-sealed.md to narrow the disallowed cases:
forbid only when the set of allowed property names is introduced or becomes
known inside composition branches or `$ref` (i.e., when the object with
`additionalProperties: false` does not itself declare the complete set of
`properties`/`patternProperties`), allow `additionalProperties: false` when all
property names are declared at that same object level even if
`oneOf`/`anyOf`/`allOf` are used to constrain values, and add an explicit
recommendation to prefer `unevaluatedProperties: false` for modular/composed
schemas.

Comment on lines +232 to +233
- **OP#12 enforcement:** When registering a derived schema, for every object position where any schema in the inheritance chain declares `"x-gts-sealed": true`, the merged set of property keys contributed by the derived schema at that position MUST be a subset of the property keys present in the chain up to that point. New keys cause registration to fail.
- **Refinement still allowed:** Derived schemas MAY narrow constraints on existing properties (e.g., reduce `maxLength`, narrow `enum`, increase `minimum`), MAY add `required`, MAY refine previously-open nested objects (recursively, subject to their own `x-gts-sealed` annotations).
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Resolve patternProperties semantics before declaring OP#12 enforcement normative.

Line 232 defines sealed checks only as properties key-subset, while lines 445–446 leave patternProperties as open. That leaves conformance behavior under-specified for sealed objects using pattern-based keys.

Suggested clarification
- the merged set of property keys ... MUST be a subset ...
+ the merged set of declared keys (`properties`, and `patternProperties` if present) ... MUST be a subset ...

Or explicitly state in this ADR that patternProperties is excluded for now and deferred to a follow-up ADR, to avoid diverging implementations.

Also applies to: 445-446

🤖 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 `@adr/0001-additional-properties-and-sealed.md` around lines 232 - 233, The
OP#12 enforcement text for "x-gts-sealed" currently only references the
"properties" key and leaves "patternProperties" ambiguous; update the ADR text
around OP#12 (the paragraphs discussing "OP#12 enforcement" and the later lines
about patternProperties) to either (a) explicitly include patternProperties in
the sealed check (e.g., state that keys matched by patternProperties are treated
as sealed and derived schemas may not introduce new pattern-matching keys), or
(b) explicitly exclude patternProperties from OP#12 and note that pattern-based
key semantics are deferred to a follow-up ADR; reference the "x-gts-sealed" /
"OP#12 enforcement" paragraphs and the later "patternProperties" mention when
making this clarification so implementations have an unambiguous normative
decision.

Comment thread adr/0001-additional-properties-and-sealed.md
…aled

- Document three concrete problems with the current spec's overloaded use of
  additionalProperties: false — allOf-applicator footguns, forward-compatibility
  break, and missing middle ground between fully-open derivation and x-gts-final
- Connect the closed content model to the tolerant reader principle (Postel's
  law), with pipeline-enrichment, vendor-extension, and producer-side-optionality
  scenarios beyond pure version evolution
- Propose x-gts-sealed as a per-schema-object derivation-control modifier
  enforced by OP#12; demote additionalProperties: false to its native JSON
  Schema instance-validation role
- Capture considered options with explicit per-option mapping of which problems
  they address and worked code demonstrations, plus impact on OP#6/OP#12 and
  reference implementations
- Place the ADR at repo-root adr/ for visibility
@aviator5 aviator5 force-pushed the additional-properties-adr branch from 33631a0 to 7137c8d Compare May 21, 2026 12:33
@aviator5 aviator5 marked this pull request as draft May 21, 2026 15:05
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.

1 participant