← stitchkitDocumentation

ADR 0205 — Flat tool collisions retain structural alternatives

  • Status: Accepted — amends the no-nested-union collision rule of ADRs 0033 and 0065; preserves the presentation/runtime boundary of ADR 0050.
  • Invariants: I3, I8.

Context

A field shared by discriminated-union variants can be an object in one variant and an array in another. Advertising only type: ['object', 'array'] discards the object vocabulary and array items. A provider requiring an item schema rejects the tool before execution. Empty items also hides the model’s contract when two array alternatives have different element structures.

Decision

The shared presentation join retains distinct structural alternatives in a property-level anyOf whenever the provable kinds include an object or array. Each alternative retains its items, properties, required keys, bounds and key policy. Structurally identical alternatives are deduplicated; shared outer descriptions and conditional-required hints belong to the joined property.

The containing discriminated union still becomes one object. Its discriminator is an enum and only fields required by every variant remain required. Plain scalar collisions retain their conservative type/enum widening. An unprovable kind remains unconstrained instead of acquiring a guessed structure.

This is a union of already projected schemas, without a Cartesian expansion or a second recursive projection pass. Every value accepted by an original branch is still accepted by the presentation; discriminator-to-field correlations are intentionally absent there. The original Zod schema alone owns runtime parsing.

Consequences

Flat mode guarantees an object for a structurally identifiable discriminated union, not the absence of every nested anyOf. A consumer must accept nested structural alternatives; disabling flatten globally is unnecessary. Manifest, MCP and Agent use the same owning implementation. No public option, runtime validator or transport-specific schema patch is added.

Tests exercise production manifests and mounted AI SDK schemas, nested arrays, nullable and optional fields, both object shapes and runtime rejection of a value belonging to the wrong discriminator branch.