Problem
The full selector contract exposed stale assertions for state pseudo-class invalidation on an ordinary non-subject compound. The assertions expected the compile-time dependency route itself to point from the changed trigger to the final selector subject.
The runtime contract deliberately separates those two steps:
- the invalidation plan identifies the compound whose state changed (
invalidation_subject, plus invalidation_ancestors for inclusive states such as :target-within);
recascade_selector_transition compares that trigger compound in the previous/current state and then visit_related_candidates follows the selector's remaining child, descendant, or sibling combinators to the final subject.
Adding forward routes to ordinary outer dependencies bypasses that separation, changes the anchor before comparison, and breaks structural invalidation such as .parent:empty + .target.
Fix
- keep ordinary
:link, :local-link, functional :local-link(...), and :target-within dependencies scoped to their changed trigger;
- assert that these outer dependencies do not carry a second forward route;
- retain reverse routes for dependencies inside relational selectors such as
:has();
- retain the existing runtime/WPT mutation gates that prove href, document URL, child, sibling, and relational subjects recascade correctly.
Acceptance
webscene_selector_parser_tests passes the complete invalidation-plan contract;
css-link-state-invalidation.html continues to cover href addition/removal across subject, sibling, and relational consumers;
css-local-link-invalidation.html continues to cover href, history URL, and effective-base changes;
- no broad subtree fallback or additional idle work is introduced.
Problem
The full selector contract exposed stale assertions for state pseudo-class invalidation on an ordinary non-subject compound. The assertions expected the compile-time dependency route itself to point from the changed trigger to the final selector subject.
The runtime contract deliberately separates those two steps:
invalidation_subject, plusinvalidation_ancestorsfor inclusive states such as:target-within);recascade_selector_transitioncompares that trigger compound in the previous/current state and thenvisit_related_candidatesfollows the selector's remaining child, descendant, or sibling combinators to the final subject.Adding forward routes to ordinary outer dependencies bypasses that separation, changes the anchor before comparison, and breaks structural invalidation such as
.parent:empty + .target.Fix
:link,:local-link, functional:local-link(...), and:target-withindependencies scoped to their changed trigger;:has();Acceptance
webscene_selector_parser_testspasses the complete invalidation-plan contract;css-link-state-invalidation.htmlcontinues to cover href addition/removal across subject, sibling, and relational consumers;css-local-link-invalidation.htmlcontinues to cover href, history URL, and effective-base changes;