Phase: Phase 16 — Engine and strategy expansion
Proposal: docs/proposals/next-phases.md §16
Priority: P3 · Size: XL
Problem
oneOf, anyOf, allOf, and $ref nodes are treated opaquely: Analyze does not flatten through them, and an uncovered leaf inside one is reported with the construct name rather than being reasoned about. Transforming across a oneOf branch is documented as out of scope.
Union-typed fields are common in mature APIs — "one of s3, gcs, or azure config" is an ordinary thing for a platform XRD to express — and today a migration that touches one has no option but jsonPatch, which is always lossy and unverifiable.
Goal
A union-typed field can be converted with a strategy the engine can reason about.
Non-goals
Full JSON Schema union semantics. Specifically not in scope: anyOf where multiple branches validate simultaneously, or allOf composition. Target the common, tractable case — a discriminated union where exactly one branch applies — and reject the rest explicitly rather than half-supporting it.
Design
A branchMap strategy: given a oneOf on both sides, map branch i on the hub to branch j on the spoke, with an optional per-branch nested rule list for when the branches' internals also differ.
Open design questions to settle before implementation, written up in this issue:
- How is the active branch identified at runtime? A discriminator field is the tractable case. Structural matching (validate the object against each branch and pick the one that passes) is more general but needs a full validator on the hot path, which is a meaningful cost in the admission critical path.
- What happens when no branch matches, or several do? A hard runtime error is consistent with the project's fail-closed posture and with how
arrayToMapByKey treats a duplicate key.
- Lossiness. Branch counts may differ; a hub branch with no spoke counterpart is a loss and must be declared.
- Coverage accounting. The leftover-field scan has to understand that a branch's leaves are claimed by the branch mapping, or every union will report as uncovered.
(4) is likely the largest piece of work and is where the estimate comes from.
Acceptance criteria
Generated by Claude Code
Phase: Phase 16 — Engine and strategy expansion
Proposal:
docs/proposals/next-phases.md§16Priority: P3 · Size: XL
Problem
oneOf,anyOf,allOf, and$refnodes are treated opaquely:Analyzedoes not flatten through them, and an uncovered leaf inside one is reported with the construct name rather than being reasoned about. Transforming across aoneOfbranch is documented as out of scope.Union-typed fields are common in mature APIs — "one of
s3,gcs, orazureconfig" is an ordinary thing for a platform XRD to express — and today a migration that touches one has no option butjsonPatch, which is always lossy and unverifiable.Goal
A union-typed field can be converted with a strategy the engine can reason about.
Non-goals
Full JSON Schema union semantics. Specifically not in scope:
anyOfwhere multiple branches validate simultaneously, orallOfcomposition. Target the common, tractable case — a discriminated union where exactly one branch applies — and reject the rest explicitly rather than half-supporting it.Design
A
branchMapstrategy: given aoneOfon both sides, map branch i on the hub to branch j on the spoke, with an optional per-branch nested rule list for when the branches' internals also differ.Open design questions to settle before implementation, written up in this issue:
arrayToMapByKeytreats a duplicate key.(4) is likely the largest piece of work and is where the estimate comes from.
Acceptance criteria
branchMapfollows the full strategy contract: params type, op, compile resolver, webhook validation, CLI fixture, docs page (docs/contributing/adding-a-strategy.md)docs/limitations.mdupdated to narrow the "treated opaquely" statement to what remains trueGenerated by Claude Code