Skip to content

🐛 Apply merge keys before schema validation#188

Merged
frenck merged 1 commit into
mainfrom
frenck/schema-merge-keys
Jul 3, 2026
Merged

🐛 Apply merge keys before schema validation#188
frenck merged 1 commit into
mainfrom
frenck/schema-merge-keys

Conversation

@frenck

@frenck frenck commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Breaking change

None for correct usage. Documents that use << merge keys are now validated against their merged shape instead of the literal << mapping, which matches what loads() returns. A schema that previously passed only because a merged-in violation was invisible will now (correctly) fail; that is fixing a validation hole, not changing valid behavior.

Proposed change

The schema validator ran on the alias-expanded AST but never applied << merge keys, so it checked the literal << mapping instead of the merged shape loads() returns. Three concrete bugs followed: a type violation carried in through a merge slipped past validation, a required property satisfied only by a merge was wrongly flagged, and the literal << key tripped additionalProperties: false.

Validation now folds merge keys in (in place, after alias expansion) with the same semantics as the fast path: explicit keys win, earlier merges win over later ones, a plain << scalar or an !!merge-tagged key is a merge while a quoted "<<" stays a literal string, and a non-mergeable value is preserved under the literal <<. Key signatures reuse the decoder's key_sig, so merge dedup here agrees with loads() (type-distinct: 1 and "1" differ). The decoder's merge module is made pub(crate) so the validator can share that helper.

Type of change

  • Dependency or tooling upgrade
  • Bugfix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Deprecation (replaces or removes a feature, with a migration path)
  • Breaking change (a fix or feature that changes existing behavior)
  • Code quality, refactor, or test-only change
  • Documentation only

Additional information

  • This PR fixes or closes issue: None
  • This PR is related to: None
  • Link to a separate documentation pull request: None

Checklist

  • I have read the AI Policy, and this pull request was not created by an autonomous agent.
  • I fully understand the code in this pull request and can explain every line, including any AI-assisted changes.
  • The change is covered by tests, and uv run pytest passes locally. A pull request cannot be merged unless CI is green.
  • uv run ruff check . and uv run ruff format --check . pass.
  • cargo fmt --check and cargo clippy --all-targets -- -D warnings pass.
  • Round-trip fidelity is preserved: an unmodified document still re-emits byte-for-byte.
  • No commented-out or dead code is left in the pull request.

If the change is user-facing:

  • Documentation under docs/ is added or updated, and docs/verify_examples.py still passes.

Copilot AI review requested due to automatic review settings July 3, 2026 10:04
@frenck frenck added the bugfix Inconsistencies or issues which will cause a problem for users or implementers. label Jul 3, 2026
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

This PR adds YAML merge-key (<<) support to schema validation. The decode::merge module is now pub(crate) so schema code can reuse it. src/schema/mod.rs adds apply_merge_keys and helper functions that, after alias expansion, resolve merge keys on mappings/sequences bottom-up, merge values into host mappings without overriding explicit keys, and preserve non-mergeable leftovers under a literal << key, aligning validation with loads()'s output shape. New Rust and Python tests cover merge violations, required/additionalProperties interaction, explicit-key overrides, and sequence-merge leftover preservation.

Sequence Diagram(s)

See hidden diagram above.

Possibly related PRs

  • frenck/YAMLRocks#90: Overlaps with this PR's use of crate::decode::merge and merge-pass logic in src/decode/mod.rs.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: applying merge keys before schema validation.
Description check ✅ Passed The description is directly aligned with the changeset and explains the validation fix and its scope.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@codspeed-hq

codspeed-hq Bot commented Jul 3, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing frenck/schema-merge-keys (0eb6ebd) with main (ca827e2)

Open in CodSpeed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR closes a schema-validation gap by applying YAML merge keys (<<) to the alias-expanded AST before running JSON Schema validation, so validation checks the same effective mapping shape that loads() returns.

Changes:

  • Apply merge-key folding to the schema-validation AST after alias expansion (src/schema/mod.rs).
  • Reuse the decoder’s key-signature logic for merge dedup by exposing the decode merge module to the crate (src/decode/mod.rs).
  • Add Python and Rust regression tests covering merged-shape validation semantics (tests/features/test_schema.py, src/schema/mod.rs).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/schema/mod.rs Applies << merge keys to the resolved AST prior to schema validation; adds Rust unit test coverage.
src/decode/mod.rs Makes the decode merge module pub(crate) so schema validation can reuse key_sig.
tests/features/test_schema.py Adds Python regression tests for merge-key behavior under schema validation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/schema/mod.rs Outdated
@codecov-commenter

codecov-commenter commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.08738% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.39%. Comparing base (a5823bc) to head (0eb6ebd).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/schema/mod.rs 97.08% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #188      +/-   ##
==========================================
+ Coverage   92.34%   92.39%   +0.05%     
==========================================
  Files          40       40              
  Lines       11504    11606     +102     
==========================================
+ Hits        10623    10723     +100     
- Misses        881      883       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The schema validator ran on the alias-expanded AST but never applied `<<` merge
keys, so it checked the literal `<<` mapping instead of the merged shape that
`loads()` returns. A type violation carried in through a merge slipped past
validation, a `required` property satisfied only by a merge was wrongly flagged,
and the literal `<<` key tripped `additionalProperties: false`.

Validation now folds merge keys in (in place, after alias expansion) with the
same semantics as the fast path: explicit keys win, earlier merges win over
later ones, a plain `<<` scalar or an `!!merge`-tagged key is a merge while a
quoted `"<<"` stays a literal string. `merge_node_into` returns the leftover to
preserve, mirroring the fast path's `merge_into`: a merge value that is a
sequence merges its mapping elements and keeps every non-mergeable element under
one literal `<<` (as a sequence), not just the first, so the validated shape
matches `loads()`. Key signatures reuse the decoder's, so merge dedup here agrees
with `loads()` (type-distinct: `1` and `"1"` differ). The decoder's `merge`
module is made `pub(crate)` so the validator can share `key_sig`.
@frenck frenck force-pushed the frenck/schema-merge-keys branch from c56468d to 0eb6ebd Compare July 3, 2026 10:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b7f92370-c699-4d86-8197-9bf83abc5ab8

📥 Commits

Reviewing files that changed from the base of the PR and between ca827e2 and 0eb6ebd.

📒 Files selected for processing (3)
  • src/decode/mod.rs
  • src/schema/mod.rs
  • tests/features/test_schema.py

Comment thread src/decode/mod.rs
use std::collections::{HashMap, HashSet};

mod merge;
pub(crate) mod merge;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document the crate-visible module.

pub(crate) mod merge is now part of the crate-visible Rust API surface; add a /// rustdoc summary.

As per coding guidelines, src/**/*.rs: Keep public Rust items documented with /// rustdoc.

Proposed fix
+/// YAML merge-key helpers shared by the decoder and schema validator.
 pub(crate) mod merge;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub(crate) mod merge;
/// YAML merge-key helpers shared by the decoder and schema validator.
pub(crate) mod merge;

Source: Coding guidelines

Comment on lines +563 to +565
def test_merge_key_violation_in_merged_value_is_caught():
"""A `<<` merge is applied before validation, so a type violation carried in
through the merge is caught (validation sees what `loads()` returns)."""

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use one-line test docstrings.

These new test docstrings span multiple lines; please collapse each to a single-line summary.

As per coding guidelines, tests/**/*.py: Every Python test has a one-line docstring describing what it verifies.

Proposed fix
-    """A `<<` merge is applied before validation, so a type violation carried in
-    through the merge is caught (validation sees what `loads()` returns)."""
+    """Verify merged type violations are caught before validation."""
...
-    """`required` met only through a `<<` merge passes, and the literal `<<` key
-    is consumed so it is not flagged by additionalProperties: false."""
+    """Verify merged keys satisfy required without tripping additionalProperties."""
...
-    """An explicit key wins over the merged one, so the explicit (valid) value is
-    what gets validated."""
+    """Verify explicit keys override merged values during validation."""
...
-    """A `<<` sequence mixing a mapping and several non-mergeable elements merges
-    the mapping and preserves every leftover (not just the first) under one
-    literal `<<`, so the validator sees the same shape `loads()` returns."""
+    """Verify merge sequences preserve all leftovers for validation."""

Also applies to: 575-577, 598-600, 609-612

Source: Coding guidelines

@frenck frenck merged commit 54dcba9 into main Jul 3, 2026
73 checks passed
@frenck frenck deleted the frenck/schema-merge-keys branch July 3, 2026 10:51
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Jul 5, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

bugfix Inconsistencies or issues which will cause a problem for users or implementers.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants