Skip to content

feat(schema): Support additionalProperties map schemas#114

Open
jan-auer wants to merge 4 commits intomainfrom
feat/additional-properties-map-schema
Open

feat(schema): Support additionalProperties map schemas#114
jan-auer wants to merge 4 commits intomainfrom
feat/additional-properties-map-schema

Conversation

@jan-auer
Copy link
Copy Markdown
Member

@jan-auer jan-auer commented Apr 7, 2026

Some options need to represent dynamic key-value mappings rather than fixed-shape objects. A concrete example is a killswitch type with arbitrary scope keys:

pub struct Killswitch {
    pub scopes: BTreeMap<String, String>,
}

Previously, the only way to define an object in a schema was with an explicit properties map — every key had to be declared up front. This made it impossible to represent dynamic mappings.

Now, schemas can use additionalProperties to describe a map where keys are dynamic but values have a known type:

{
  "scopes": {
    "type": "object",
    "additionalProperties": {
      "type": "string"
    },
    "optional": true
  }
}

The validation layer now recognises this pattern and skips the automatic required/additionalProperties: false injection that would otherwise break it.

`inject_object_constraints()` unconditionally injected `required` and
`additionalProperties: false` into every object-typed schema property. This broke
dynamic map schemas like `{"type": "object", "additionalProperties": {"type": "string"}}`,
which are valid but have no fixed property names to enumerate as required.

The fix: skip constraint injection when the schema already declares `additionalProperties`.
The schema author controls validation explicitly in that case.

The namespace meta-schema (`namespace-schema.json`) is updated to allow
`additionalProperties` on both top-level and nested object properties, restricted to
scalar value types. A `not: {required: [additionalProperties]}` guard ensures the
existing `required`/`additionalProperties: false` injection path only applies to
structured objects, not dynamic maps.
@jan-auer jan-auer force-pushed the feat/additional-properties-map-schema branch from 9e684c8 to 2ac74d3 Compare April 9, 2026 10:15
@linear-code
Copy link
Copy Markdown

linear-code bot commented Apr 9, 2026

Some options need to represent dynamic key-value mappings rather than
fixed-shape objects. This adds support for schemas that use
`additionalProperties` to describe maps with dynamic keys and a known
value type (e.g. `BTreeMap<String, String>`).

Previously, `inject_object_constraints()` would unconditionally inject
`required` and `additionalProperties: false` into every object-typed
property, which broke any schema that already declared
`additionalProperties`. Now, `required` is still injected from declared
`properties`, but `additionalProperties: false` is only injected when the
schema doesn't already declare it.

The namespace meta-schema is updated to allow `additionalProperties` on
object properties, restricted to scalar value types via a shared
`$defs/map_value_type` definition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jan-auer jan-auer force-pushed the feat/additional-properties-map-schema branch from eae10f8 to b26b08c Compare April 9, 2026 10:33
@jan-auer jan-auer marked this pull request as ready for review April 9, 2026 10:33
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6b6ae28. Configure here.

Shape fields with `type: "object"` now require `additionalProperties` to
be declared explicitly, matching the same constraint already enforced on
top-level option definitions and `items` definitions.

Without this, a shape entry like `{"type": "object"}` would pass
meta-schema validation but produce an unconstrained object at runtime
since `inject_object_constraints` does not recurse into shape entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.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