fix(feature-model): don't mis-report a shared child as a cycle (REQ-269)#716
Merged
Conversation
📐 Rivet artifact delta
Graphgraph LR
REQ_269["REQ-269"]:::modified
classDef added fill:#d4edda,stroke:#28a745,color:#155724
classDef removed fill:#f8d7da,stroke:#dc3545,color:#721c24
classDef modified fill:#fff3cd,stroke:#ffc107,color:#856404
classDef overflow fill:#e2e3e5,stroke:#6c757d,color:#495057,stroke-dasharray: 3 3
Modified
Posted by |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
`validate_tree`'s cycle detection used a single global `visited` set with a
BFS and reported EVERY second encounter of a node as a cycle. A feature
reachable through two parents — a diamond / DAG, e.g. `root -> {a, b}` with
`a -> shared` and `b -> shared` — was enqueued from both parents, dequeued
twice, and wrongly rejected with "cycle detected involving feature `shared`".
Real feature models legitimately share a sub-feature under two branches.
Replace the BFS with an iterative DFS that tracks the current path (the
recursion stack) separately from fully-explored nodes:
- A node re-encountered while still ON the current path is a genuine cycle
(a back edge — a feature that is its own ancestor). Still rejected.
- A node reached again by a disjoint path (a shared child) is fully
explored, so it is skipped, not flagged.
The parent link remains last-writer-wins (unchanged, acceptable); only the
traversal's cycle criterion changes.
Tests: un-ignored `diamond_shared_child_is_not_a_cycle` (now passing) and
added `genuine_cycle_is_rejected` as a guard that real cycles still error.
Confirmed with the full feature_model + variant suites (proptest 14, lib
feature_model 62, variant_gap_check 2, variant_phase2 8 — all green),
clippy --all-targets clean, and the wasm seam builds
(`cargo build -p rivet-cli --features wasm` OK — composition core touched,
WIT/component unchanged so no cargo-component rebuild needed).
Fixes: REQ-269
Verifies: REQ-269
Refs: REQ-266
avrabe
force-pushed
the
fix/req-269-diamond-cycle
branch
from
July 16, 2026 15:04
4a47e63 to
6ab32f7
Compare
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Rivet Criterion Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.
| Benchmark suite | Current: 6ab32f7 | Previous: ab9b24c | Ratio |
|---|---|---|---|
traceability_matrix/1000 |
59877 ns/iter (± 349) |
43980 ns/iter (± 781) |
1.36 |
query/10000 |
339650 ns/iter (± 5633) |
223850 ns/iter (± 972) |
1.52 |
This comment was automatically generated by workflow using github-action-benchmark.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
REQ-269 — shared child (diamond/DAG) no longer mis-reported as a cycle (v0.29.0)
Surfaced by REQ-266's coverage.
validate_tree's cycle detection used a singleglobal
visitedset + BFS and reported every second encounter of a node asa cycle. A feature reachable through two parents — a diamond,
root -> {a, b}with
a -> sharedandb -> shared— was enqueued from both parents, dequeuedtwice, and wrongly rejected:
cycle detected involving featureshared``. Realfeature models legitimately share a sub-feature under two branches.
Fix
Iterative DFS tracking the current path (recursion stack) vs fully-explored
nodes:
is its own ancestor). Still rejected.
not flagged.
Parent link stays last-writer-wins (unchanged, acceptable); only the traversal's
cycle criterion changes.
Tests
diamond_shared_child_is_not_a_cycle— now passes.genuine_cycle_is_rejected—root -> mid -> rootmust still error.Verification
Full feature-model + variant suites green (proptest 14, lib feature_model
62, variant_gap_check 2, variant_phase2 8);
clippy --all-targets(1.97.0) clean; wasm seam builds —
cargo build -p rivet-cli --features wasmOK (composition core touched, WIT/component unchanged so no cargo-component
rebuild).
Fixes: REQ-269 · Refs: REQ-266
🤖 Generated with Claude Code