until on reticle_act_and_wait, and to predicate on reticle_assert and reticle_wait_for.
Every predicate is { kind, ...fields }.
Not all predicates prove the same thing
Reticle grades them, and it will tell you when you have chosen a weak one. This is a real response from atext assertion:
element and text are convenient and weak; signal, state and net are what make a green trustworthy.
The leaves
element
query accepts role, name, text, label, placeholder, testid, alt, scope. state is one of visible, hidden, enabled, disabled, checked, expanded, focused, present.
Add "absent": true to assert something is gone. The predicate for a removal, a dismissed toast, or a regression check.
text
contains and value are accepted. absent: true asserts the text is not present.
net
route
contains also works.
console
signal
reticle.signal(). dataMatches is shallow JSON matching, and * means “present, any value”.
Nothing outranks this. A 200 proves the server was reachable; a rendered row proves React ran. A signal is the application itself saying the thing succeeded. See instrumentation for how to emit them.
state
equals takes a literal, or an operator pattern:
Omit
equals entirely to assert presence.
This is the predicate that catches a UI-versus-store desync. A deploy that only looks shipped. Deterministically, in one call, with no model involved. On a miss it names the real store value and the keys that were available, so a failure is legible rather than a blind “no”.
animation
Combinators
allOf is the workhorse. “The signal fired and exactly one request went out and the console stayed clean” is a genuinely strong check, and it is one call.
Each child reports its own evidence:
Timing and scoping
The
since cursor comes from a prior reticle_act response. Use it whenever you are asserting something an action was supposed to cause. Without it, an assertion can pass on an event from two clicks ago.
Where predicates get used
Naming the consequence before the action is the whole point.