fix(validate,coverage): required-backlink matches inverse-name convention + honours alternate-backlinks#351
Merged
Conversation
…tion + honours alternate-backlinks (#349) `Backlink.link_type` stores the *forward* link-type name (e.g. `supports`) while schemas like `safety-case.yaml` write `required-backlink` as the *inverse* name (e.g. `supported-by`). The strict equality match at `validate.rs:906` and `coverage.rs:200` therefore never matched for any inverse-name rule — `goal-has-support` fired for every safety goal even when correctly supported by a solution, and coverage counted every such goal as uncovered. `dev.yaml`'s `required-backlink: satisfies` happens to use the forward name and worked; the bug surfaced only on schemas following the inverse-name convention. Match now accepts either spelling at both call sites: `bl.link_type == name || bl.inverse_type.as_deref() == Some(name)`. Same fix path additionally evaluates `rule.alternate_backlinks` — `validate.rs` previously ignored that field outright, so a safety goal satisfied only via an alternate (e.g. `decomposed-by` from a strategy instead of `supported-by`) still erroneously fired the rule. Four regression tests pin the behaviour (two per engine): one for the inverse-name match, one for alternate-backlinks. Reproduces and clears the loom safety-case scenario from the bug report. Fixes: REQ-004 Refs: #349
📐 Rivet artifact deltaNo artifact changes in this PR. Code-only changes (renderer, CLI wiring, tests) don't touch the artifact graph. |
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: 015037a | Previous: cb670c8 | Ratio |
|---|---|---|---|
link_graph_build/10000 |
37966689 ns/iter (± 3420341) |
31257613 ns/iter (± 2082341) |
1.21 |
This comment was automatically generated by workflow using github-action-benchmark.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
# Conflicts: # CHANGELOG.md
This was referenced May 31, 2026
avrabe
added a commit
that referenced
this pull request
May 31, 2026
…363) Mark-implemented sweep — acceptance met by merged code: - REQ-105 (HTML export self-containment, v0.14.0) draft -> implemented - REQ-110/111 (coverage relabel, #348) draft -> implemented - REQ-124 (diagnostic remediation, v0.14.0) approved -> implemented Also re-files REQ-131 (the #349 required-backlink fix, status implemented): its artifact was dropped when #351 was reworked to add alternate-backlink support, so the code shipped on main without the requirement. Restores the traceability that issue comments on #349/#350 reference. rivet validate PASS. Refs: REQ-105, REQ-110, REQ-111, REQ-124, REQ-131 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Closes #349.
The bug
Backlink.link_typestores the forward link-type name (e.g.supports) while several built-in schemas — notablyschemas/safety-case.yaml— writerequired-backlinkusing the inverse name (e.g.supported-by). The strict equality match atrivet-core/src/validate.rs:906andrivet-core/src/coverage.rs:200therefore never matched for any inverse-name rule.Observable effect on the loom safety case reported in the issue:
…fired for every safety goal even though every one of them was correctly supported (
SOL-1 supports → SG-1, etc.).dev.yaml'srequired-backlink: satisfieshappens to use the forward name and worked; the bug surfaced only on schemas following the inverse-name convention.The fix
Both call sites now accept either spelling:
Same fix path additionally evaluates
rule.alternate_backlinks—validate.rspreviously ignored that field outright, so a safety goal satisfied only via an alternate (e.g.decomposed-byfrom a strategy instead ofsupported-by) still erroneously fired the rule.coverage.rshad the same gap. The match helper is now closed over the backlinks, called once for the primary and once per alternate; the rule passes when any shape matches.Acceptance criteria from the issue
The issue lists a primary fix, a secondary bug, and four frictionless-ness suggestions. This PR addresses the two binding items; the frictionless suggestions are intentionally split out as a follow-up (per "one PR per issue, keep PRs focused" — they touch the CLI's
rivet getrendering and require a newrivet whycommand, which is a separate feature).validate.rsvalidate.rs:902–941coverage.rscoverage.rs:194–225rule.alternate_backlinksinvalidate.rs(secondary bug)validate.rs:931–935rule.alternate_backlinksincoverage.rscoverage.rs:222–225rivet getshows inbound backlinksrequired-backlinkfailure message lists found backlinksrivet why <id> <rule>explain modeTests
Four regression tests pin the fix — two per engine:
validate::tests::required_backlink_inverse_name_is_satisfied_by_forward_link— reproduces the loom GSN scenario exactly (rule namessupported-by, artifact has forwardsupports); without the fix the rule fires, with the fix it does not.validate::tests::validate_honours_alternate_backlinks— goal satisfied only via the alternate (decomposed-byfrom a strategy) must not fire the rule.coverage::tests::required_backlink_matches_inverse_link_type_name— same scenario from the coverage engine's perspective; without the fixcovered = 0, with the fixcovered = 1.coverage::tests::coverage_honours_alternate_backlinks— alternate-satisfied goal counts ascovered, notuncovered.Full
cargo test -p rivet-core --lib→ 1086 passed, 0 failed.rivet validateon the rivet repo itself → PASS unchanged.End-to-end verification
Reproduced and cleared the loom-style scenario on a minimal safety-case project (
SG-1supported bySOL-1;SG-2decomposed bySTRAT-1;SG-3with neither):Only the genuinely-unsupported
SG-3errors —SG-1(primary backlink path) andSG-2(alternate-backlink path) both pass, which matches the user's expectation in the bug report.Files touched
rivet-core/src/validate.rs— comparison fix +alternate_backlinksevaluation + 2 regression testsrivet-core/src/coverage.rs— comparison fix +alternate_backlinksevaluation + 2 regression testsCHANGELOG.md— Unreleased entryCommit trailers:
Fixes: REQ-004,Refs: #349.Generated by Claude Code