Machine.deleteRule ghost/strand defect also affects anything-but wildcard patterns (follow-up to #255/#256)
- Dominant language
- Java
- Stars
- 615
- Forks
- 82
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 7
Description
## Describe the bug
#256 fixed `deleteRule` isolation for rules sharing byte-identical wildcard clauses (ghost: deleted rule keeps matching; strand: surviving sibling stops matching — see #255). The same defect class also affects `anything-but: {wildcard: ...}` patterns and is not covered by that fix.
Mechanism: `ByteMachine.findAllPatterns` routes `ANYTHING_BUT_WILDCARD` (and the other `ANYTHING_BUT*` types) to the `default:` single-NameState path. Anything-but-wildcard values are wildcard-parsed into the ByteMachine, so the add path's refusal to reuse indeterminate-prefix / self-loop composite states (`canReuseNextByteState`) applies — byte-identical anything-but-wildcard clauses accumulate one `NameState` per rule over shared transitions, and the single-NameState delete path picks an arbitrary one (ghost) or tears down shared transitions still referenced by a sibling (strand).
This is pre-existing: the repro below fails identically on `main` before and after #256 — #256 did not introduce or worsen it.
## To Reproduce
Non-deterministic ~50% per trial (HashSet iteration order), so run over many fresh machines:
```java
@Test
public void anythingButWildcard_sharedClause_deleteIsolation() throws Exception {
String ruleJson = "{\"name\":[\"test\"],\"properties.foo\":[{\"anything-but\":{\"wildcard\":\"*bar*\"}}]}";
String event = "{\"name\":\"test\",\"properties\":{\"foo\":\"nomatchhere\"}}";
for (int i = 0; i < 200; i++) {
Machine machine = Machine.builder().build();
machine.addRule("rule1", ruleJson);
machine.addRule("rule2", ruleJson);
assertEquals(2, machine.rulesForJSONEvent(event).size());
machine.deleteRule("rule1", ruleJson);
List after = machine.rulesForJSONEvent(event);
assertFalse("rule1 should be deleted (ghost)", after.contains("rule1"));
assertTrue("rule2 should still match (strand)", after.contains("rule2"));
}
}
```
Also reproduces with different-shaped rules sharing the same anything-but-wildcard clause on one field.
## Expected behavior
Same as #255: `deleteRule` removes only the named rule; siblings sharing the clause keep matching; result independent of add order.
## Proposed fix
Extend `findAllPatterns` to cover the `ANYTHING_BUT*` types (and audit `NUMERIC_RANGE`). The anything-but find paths are multi-value — each value in the set is looked up individually and the results are asserted to converge to a single NameState (`findAnythingButPattern` / `findAnythingButValuesSetPattern`) — so the extension needs per-value `findAllMatchPattern` with union semantics rather than the simple single-value delegation #256 added for value patterns. The teardown guard from #256 (`noNameStateContainsPattern`) already generalizes.
I'll take this as a stacked change on top of #256.
## Environment
- event-ruler current `main` (also reproduces on the #256 branch and on 1.8.x)
Contributor guide
Research direction
Start at ByteMachine.findAllPatterns and trace the ANYTHING_BUT_WILDCARD paths through findAnythingButPattern and findAnythingButValuesSetPattern, including canReuseNextByteState and noNameStateContainsPattern. Add and run the anythingButWildcard_sharedClause_deleteIsolation test over fresh machines, then verify that deleting one rule leaves only its sibling matching regardless of add order.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100