Declarative Action Postconditions
After an action an agent (or a replay harness) usually has a concrete expectation: “a dialog
saying ‘Saved’ should appear AND the Submit button should disable”. expect_poll /
assert_eventually poll a single condition but have no notion of an action-bound
postcondition spec, and they don’t diff against a before baseline (so they cannot express
“a NEW dialog appeared” — only “a dialog exists”). trajectory_eval rubrics are
whole-trajectory, not per-step screen state. postcondition fills the gap: a small JSON spec
of clauses evaluated against the after-observation (optionally diffed against the
before-observation), returning a per-clause pass/fail report.
Clauses: appears / disappears (diffed against before), enabled / disabled,
text_present / text_absent, and count (equals / min). Pure-stdlib over
element dicts; the spec is plain JSON so it rides into action files / MCP / the scheduler.
Imports no PySide6.
Headless API
from je_auto_control import check_postcondition, compile_postcondition
spec = {"appears": {"role": "dialog", "name": "Saved"},
"disabled": {"name": "Submit"}}
report = check_postcondition(after_elements, spec, before=before_elements)
if not report.ok:
print("failed clauses:", report.failed)
# turn a spec into a predicate to drive expect_poll
predicate = compile_postcondition({"text_present": "Saved"})
check_postcondition returns a PostconditionReport (ok / clauses —
[{type, ok, detail}] — / failed). appears succeeds only when the element is in
after and not in before (a genuinely new element); disappears requires a
before frame. compile_postcondition returns an after -> bool predicate for pairing
with expect_poll / assert_eventually.
Executor command
AC_check_postcondition (after / spec / before →
{ok, clauses, failed}) is exposed as the MCP tool ac_check_postcondition (read-only)
and as the Script Builder command Check Postcondition under Native UI.