What a Reflex actually is
A Reflex is a declarative reactive trigger. You describe — in YAML, in Context Flux (CX) — the structural and state conditions you want Contexta to notice, and the engine fires a webhook with full provenance the moment those conditions hold.
The point of the DSL is that you stop writing imperative pollers and start describing what matters. Once you write it down, the engine takes care of the rest: matching nodes against the pattern, watching state transitions, evaluating composition trees, and firing with hop-by-hop reasoning attached to the payload.
Every Reflex has two parts:
cx— the condition in CX. Handles structural pattern matching, state transitions, negation, aggregation, correlation, and reachability.filter(optional) — runtime predicates that sit outside graph topology, like age-relative conditions or sliding windows on non-state properties.
That is the whole surface area. No specialised trigger types, no separate engines for cron vs reactive. CX is the universal language; on: controls when it is evaluated.
The unified schema
reflex:
name: string
description: string
trigger:
# REQUIRED — condition in CX
cx: CxStatement | GraphletId | GraphletId[]
# OPTIONAL — runtime predicates outside graph topology
filter:
<variable.property>: { older_than | within | eq | gt | lt | in: value }
# OPTIONAL — when to evaluate (default: node(*))
on: cron(...) | node(type[:qualifier]) | edge(type[:qualifier])
# Webhook endpoint — receives full context on every fire
action:
url: string
payload: object
cooldown: duration
Every webhook delivery automatically includes affected_entities, the reasoning_chain traversal, previous_value + change_delta for state transitions, and any related_decisions linked in the graph. That payload is the entire point — your agent does not have to call back to figure out what just happened.
Match and act, one example at a time
The smallest non-trivial Reflex watches for a structural condition plus a state transition on the same entity. Here is the bankrupt-supplier classic — three lines of CX expressing a pattern that is impossible to write in flat IF/THEN rules:
reflex:
name: supplier_status_alert
description: Fire when a supplier flips to bankrupt status.
trigger:
cx: (c:company) -[:supplies]-> (us) AND (c @status: * -> "bankrupt")
on: node(company:updated)
action:
url: https://hooks.example.com/supplier
cooldown: 1h
The CX statement reads in two halves:
(c:company) -[:supplies]-> (us)is the Graphlet — the structural pattern. Contexta's compiler stores this as a graph node, and any company that supplies to "us" gets a single-hop:member_ofedge pointing at it. Finding affected entities is a graph traversal, not a CX re-evaluation.(c @status: * -> "bankrupt")is the StateWatch — the state transition. It rides on thecvariable from the Graphlet, so the watch is naturally scoped to the matched suppliers.
When PersistGraphDelta writes a new company status, ApplyGraphletBindings decides whether the entity belongs to the Graphlet. EvaluateAffectedGraphlets checks the StateWatch, walks the composition tree, and decides whether to fire. The whole loop is incremental — only Graphlets touched by the delta are re-evaluated, never the full Reflex catalogue.
Correlation across systems
The real power of CX is correlation. The next example fires when three independent negative signals — a low NPS score, a critical support ticket, and a usage decline — point at the same customer inside a 14-day window. Each signal lives in a different upstream system; only the graph can link them to the same entity.
reflex:
name: churn_risk_convergence
description: >
Fires when a customer submits a low NPS score, files a critical
support ticket, and shows declining usage — all within 14 days.
trigger:
cx: >
(c:customer) -[:submits]-> (:nps_survey @score: < 6)
WITHIN "14d" OF
(c) -[:files]-> (:support_ticket @priority: "critical")
AND (c @metrics.weekly_active_users_delta: < -20)
filter:
c.renewal_date: { within: 60d }
on: node(customer:updated)
action:
url: https://hooks.example.com/cs-escalation
cooldown: 7d
WITHIN "14d" OF is the temporal correlation operator. AND joins it with a scalar drift check on the customer's weekly-active-users metric. filter adds a runtime predicate that the renewal date is inside the next 60 days — a condition that has no meaning at graph-write time, only at evaluation time.
That is one Reflex covering three product surfaces, with the convergence logic written once and evaluated incrementally. The webhook delivers the full hop chain, so the CS lead receiving the page knows exactly which NPS survey, which support ticket, and which usage delta converged on this customer.
Loci and Motifs
Two more vocabulary words and you can read the rest of the reference.
- A Motif is the canonical name for the building block CX expressions decompose into — Graphlets and StateWatches are both Motifs. Every Motif is a graph node; every Reflex is an AST over those nodes. This means Motifs are reusable: register
gl_supplier_roleonce and reference it by ID from any number of Reflexes. - A Locus is the historical lens. Every Motif has a Locus that records when it bound, when it unbound, and what state transitions it witnessed. Auditing a Reflex firing is reading the relevant Locus — there is nowhere else the history could be.
reflex:
name: shared_supplier_alert
trigger:
cx:
- gl_supplier_role # reusable Motif from the catalogue
- gl_bankrupt_status
on: node(company:updated)
action:
url: https://hooks.example.com/supplier
That is two Motif IDs glued together — no inline CX needed. Reuse compounds: as the catalogue grows, Reflex authors increasingly compose existing Motifs instead of writing new CX from scratch.
Where to next
The DSL handles structural matching, negation, aggregation, correlation, scheduled evaluation, and runtime filters with one schema. The CLI gives you contexta reflex compile, contexta reflex dry-run, and contexta reflex tail so you can see your CX decompose into Motifs, watch the Locus bind in real time, and replay historical firings against the current graph.
Once you have written your first Reflex, you have changed the contract with your agent. The agent stops asking "what changed?" on every turn. The harness tells it — declaratively, reactively, and with provenance.