Skip to content
Contexta
Blog

Reflexes 101 — declarative triggers for agent memory

Contexta Team9 min read
  • reflexes
  • cx
  • tutorial

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_of edge 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 the c variable 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_role once 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.

About the author

Contexta Team

The Contexta team ships the context harness for production AI agents — persistent memory, declarative Reflexes, and verifiable provenance, all in one substrate.

Keep reading

More field notes from the harness.

7 min read

Why context is the harness

  • strategy
  • architecture
  • context

Agents do not fail at retrieval — they fail at context. The harness is what holds an agent's reasoning together when memory, reactivity, and provenance work as one substrate.

8 min read

Provenance over similarity

  • provenance
  • recall
  • audit

Vector similarity finds what looks the same; provenance proves what is true. The hybrid recall score in Contexta blends three signals — semantic, structural, provenance — and tunes per namespace.

Wire Contexta into your agent.

Spin up a workspace, drop the SDK in, and turn passive memory into reactive context.