Skip to content

🐛 Validate anchors and aliases on the composer paths too#194

Merged
frenck merged 1 commit into
mainfrom
frenck/composer-validate-anchors
Jul 3, 2026
Merged

🐛 Validate anchors and aliases on the composer paths too#194
frenck merged 1 commit into
mainfrom
frenck/composer-validate-anchors

Conversation

@frenck

@frenck frenck commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Breaking change

Documents that were already invalid now raise on the round-trip and includes paths instead of being silently accepted (an undefined *alias used to resolve to None there; a malformed anchor/tag was accepted). No valid document changes behavior. This aligns the composer paths with the fast/annotated paths and PyYAML, so it removes an inconsistency rather than adding one.

Proposed change

The fast decoder rejects three malformed anchor/alias constructs; the round-trip composer (which backs OPT_ROUND_TRIP and OPT_INCLUDES) did not, so the same document was a hard error on the fast and annotated paths but silently accepted on the composer paths:

  • an unknown or forward *alias (b: *ghost) resolved to a silent None instead of erroring, so a typo in an alias name became data loss;
  • an alias carrying its own anchor or tag (&b *a, !!str *a) was accepted;
  • a node with two anchors (&a\n&b value) was accepted.

The composer now enforces all three, matching the fast decoder and PyYAML. It tracks the anchors defined so far in the current document (recording each only after its children compose, so a self-reference is still an unknown reference) and clears that set at every document boundary, so an anchor cannot leak across documents either. Valid aliases are unaffected; all load paths now agree.

Found by a differential cross-path consistency sweep.

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

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.

The fast decoder rejects three malformed anchor/alias constructs; the round-trip
composer (which also backs `OPT_ROUND_TRIP` and `OPT_INCLUDES`) did not, so the
same document was a hard error on the fast and annotated paths but silently
accepted on the composer paths:

- an unknown or forward `*alias` (`b: *ghost`) resolved to a silent `None`
  instead of erroring, so a typo in an alias name became data loss;
- an alias carrying its own anchor or tag (`&b *a`, `!!str *a`) was accepted;
- a node with two anchors (`&a\n&b value`) was accepted.

The composer now enforces all three, matching the fast decoder and PyYAML. It
tracks the anchors defined so far in the current document (recording each only
after its children compose, so a self-reference is still an unknown reference)
and clears that set at every document boundary, so an anchor cannot leak across
documents either. Valid aliases are unaffected; all load paths now agree.
Copilot AI review requested due to automatic review settings July 3, 2026 13:55
@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

Warning

Review limit reached

@frenck, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8158b780-b353-47ee-a860-a2e62a5e0062

📥 Commits

Reviewing files that changed from the base of the PR and between 88e74b6 and d8d36df.

📒 Files selected for processing (3)
  • src/roundtrip/anchors.rs
  • src/roundtrip/composer.rs
  • tests/core/test_anchors.py

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/composer-validate-anchors (d8d36df) with main (88e74b6)

Open in CodSpeed

@codecov-commenter

codecov-commenter commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.57%. Comparing base (dce8e64) to head (d8d36df).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #194      +/-   ##
==========================================
+ Coverage   92.44%   92.57%   +0.12%     
==========================================
  Files          40       40              
  Lines       11872    11981     +109     
==========================================
+ Hits        10975    11091     +116     
+ Misses        897      890       -7     

☔ 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.

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 cross-path consistency gap in anchor/alias validation. The fast decoder (loads/dumps default path) already rejects three malformed constructs — an unknown/forward *alias, an alias carrying its own &anchor/!tag, and a node with two anchors — but the round-trip composer (backing OPT_ROUND_TRIP, OPT_INCLUDES, the annotated multi-document path, and schema validation) silently accepted them, resolving an undefined alias to None. The composer now enforces the same rules by tracking anchors seen so far in the current document (anchors_seen), recording each anchor only after its children compose (so a self-reference is still unknown) and clearing the set at every document boundary. This is an intentional breaking change: documents that were already invalid now error consistently across all load paths, matching the fast decoder and PyYAML.

Changes:

  • Add anchors_seen to Composer, reject unknown/forward aliases, aliases with an anchor/tag, and duplicate anchors, clearing anchor scope at each document start.
  • Update the cyclic_anchor_terminates_via_depth_cap unit test to build the cyclic AST by hand (compose now rejects it) and assert the composer refuses the document form.
  • Replace the obsolete "delete unrelated key with preexisting dangling alias" test with cross-load-path tests for unknown aliases and malformed anchor/tag cases.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/roundtrip/composer.rs Adds anchors_seen tracking and the three validation checks, mirroring the fast decoder; clears anchor scope at document boundaries.
src/roundtrip/anchors.rs Rewrites the cyclic-anchor test to construct the cycle directly (since composing it now errors) and asserts the composer rejects the document form.
tests/core/test_anchors.py Removes the now-impossible dangling-alias delete test; adds cross-path tests for unknown aliases and malformed anchor/tag constructs.

I verified parity with the fast decoder (src/decode/mod.rs): the two-anchor, alias-with-anchor/tag, unknown-alias, and cross-document clearing logic all match, HashSet is imported, mapping keys and values both flow through compose_node (so key anchors are recorded), the empty-anchored-node case still records its anchor, error messages surface correctly on all paths (including includes via a located YAMLRocksParseError), and the reworked Rust test compiles against the current Span/ScalarStyle. No issues found.


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

@frenck frenck merged commit dfa7ec3 into main Jul 3, 2026
74 of 75 checks passed
@frenck frenck deleted the frenck/composer-validate-anchors branch July 3, 2026 14:15
@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