Skip to content

re-frame.machines

State machines, per Spec 005. You register a machine with one macro (reg-machine), and the machine is an event handler. Its transition table — a map of :states, :on, :entry, :exit, :after — compiles into a reg-event handler at registration time. Dispatch an event at the machine's id, and the table decides the transition. The resulting :db and :fx flow through the normal cascade. The same trace bus, time-travel, and override surfaces that work for plain handlers also work for machines.

(:require [re-frame.core     :as rf]           ;; reg-machine / defmachine — the facade registration macros
          [re-frame.machines :as rf.machines]) ;; engine, query, transition, tooling, runtime helpers

Read a machine's snapshot with the ordinary subscribe, naming its framework sub vector. Use @(rf/subscribe [:rf/machine machine-id]) for the snapshot, and @(rf/subscribe [:rf.machine/has-tag? machine-id tag]) for a :tags-membership predicate. There is no named-read-sugar fn: every runtime-db framework read is a subscription vector, one grammar.

Surfaces split two ways:

  • re-frame.core facade exports (reach as rf/…): the reg-machine / defmachine registration macros.
  • Owned by re-frame.machines (reach as rf.machines/<name>, not rf/<name>rf.machines is the canonical alias for a framework subsystem namespace, per Conventions §Require-alias dialect; the bare machines is reserved for an app's own namespaces): the plain-fn registration / engine / query helpers (reg-machine*, make-machine-handler, machine-transition, machines) and the implementation-tier runtime helpers. This namespace is the day8/re-frame2-machines optional artefact.

Cross-machine messaging is plain [:dispatch [<actor-id> <event>]] — a machine IS an event handler, so its id is its address.

For the full treatment — the underlying model, the recognition kit, and the rationale behind the capability subset — see the machines concept guide.

Registration

reg-machine

  • Kind: macro
  • Signature:
    (reg-machine machine-id machine-spec)
    (reg-machine machine-id opts machine-spec)
    
  • Description: The canonical registration macro. Compiles the spec into a reg-event handler and captures per-element source for Xray.
    • Walks the literal spec at expansion time. It attaches per-element source ({:fn .. :source-coords .. :source-code ..}) to each :guards / :actions entry, and a reference-site :source-coords to each :states-tree map node (state-node or transition map). Xray uses these to navigate from a snapshot back to the guard or action definition, or to the state-node.
    • Top-level call-site coords land on handler-meta.
    • The optional opts registration-metadata map sits in the middle slot. Its :schema key validates the dispatched outer event vector at the :where :event boundary. Any other keys ride onto the registration metadata.
    • The framework-owned :rf/machine? / :rf/machine keys are stamped by the registration home and must not appear in opts.
    • Reached on the re-frame.core facade as rf/reg-machine.

A minimal machine:

(rf/reg-machine :session
  {:initial :anonymous
   :data    {:credentials nil}

   :actions
   {:capture-credentials
    ;; Remember who's signing in so the snapshot carries it through the flow.
    (fn [{[_ creds] :event}]
      {:data {:credentials creds}})

    :issue-auth
    ;; Fire the login request; the reply loops back as :auth-ok / :auth-fail.
    (fn [{[_ creds] :event}]
      {:fx [[:rf.http/managed
             {:request    {:method :post :url "/api/login" :body creds
                           :request-content-type :json :sensitive? true}
              :decode     :json
              :on-success [:session [:auth-ok]]
              :on-failure [:session [:auth-fail]]}]]})}

   :states
   {:anonymous      {:on {:login {:target :authenticating
                                  :action :capture-credentials}}}
    :authenticating {:entry :issue-auth
                     :after {500 {:target :timeout}}      ;; ms — auth taking too long
                     :on    {:auth-ok   {:target :authenticated}
                             :auth-fail {:target :anonymous}}}
    :authenticated  {:on {:logout {:target :anonymous}}}
    :timeout        {:on {:retry  {:target :anonymous}}}}})

;; The machine IS an event handler — dispatch a wrapped event at its id.
(rf/dispatch [:session [:login {:user "alice" :pass "correct-horse"}]])

The snapshot lives at [:rf.runtime/machines :snapshots :session] in the frame's runtime-db partition (not app-db). The shape is {:state :anonymous :data {...}} (plus framework-managed slots for :after timer epochs and tags). Read it via the [:rf/machine machine-id] subscription vector — @(rf/subscribe [:rf/machine machine-id]) — or directly with subscribe-once.

defmachine

  • Kind: macro
  • Signature:
    (defmachine name machine-spec)
    (defmachine name docstring machine-spec)
    
  • Description: Defines a machine-spec value with per-element source captured. It is a drop-in for def whose body is a literal machine-spec map.
    • Walks the literal spec at expansion time. It attaches per-element source ({:fn .. :source-coords .. :source-code ..}) to each :guards / :actions entry, and a reference-site :source-coords to each :states-tree map node.
    • The source is stamped on the value itself. When the value is later passed to reg-machine, (rf/handler-meta {:source :store :kind :machine-guard :id [machine-id guard-id]}) and the Xray machine-cascade source rendering light up for value-registered machines exactly as for inline ones.
    • Needed because a plain (def m {…}) + (reg-machine :id m) hands reg-machine only the symbol, so its literal-walk captures nothing. defmachine captures at the definition site.
    • The dev-only :source-* slots DCE under :advanced + goog.DEBUG=false.
    • Reached on the re-frame.core facade as rf/defmachine.
  • Example:
    ;; Capture per-element source at the def site, then register the value.
    (rf/defmachine door-machine
      "A door that locks."
      {:initial :locked
       :states  {:locked {:on {:unlock {:target :closed}}}
                 :closed {:on {:open {:target :open}
                               :lock {:target :locked}}}
                 :open   {:on {:close {:target :closed}}}}})
    
    (rf/reg-machine :door/main door-machine)
    

re-frame.machines/reg-machine*

  • Kind: function (owned by re-frame.machines — not a re-frame.core facade export)
  • Signature:
    (re-frame.machines/reg-machine* machine-id machine-spec)
    (re-frame.machines/reg-machine* machine-id opts machine-spec)
    
  • Description: Plain-fn surface beneath the macro. No source-coord walking.
    • For code-gen pipelines, REPL workflows, or conformance harnesses that synthesise specs from data.
    • The 3-arity takes the same middle-slot opts registration-metadata map as the macro. :schema validates the dispatched outer event vector at the :where :event boundary. The framework-owned :rf/machine? / :rf/machine keys must not appear in opts.
  • Example:
    ;; Same effect as the macro, minus the source-coord walking.
    (rf.machines/reg-machine* :traffic-light
      {:initial :red
       :states  {:red   {:on {:go {:target :green}}}
                 :green {:on {:go {:target :red}}}}})
    

re-frame.machines/make-machine-handler

  • Kind: function (owned by re-frame.machines — not a re-frame.core facade export)
  • Signature:
    (re-frame.machines/make-machine-handler spec)  event-handler fn
    
  • Description: Compiles a transition table into the event-handler fn that reg-machine would register. Returns the fn; does not register it.
  • Example:
    ;; Build the handler fn without registering it (e.g. to inspect or compose it).
    (def handler
      (rf.machines/make-machine-handler
        {:initial :idle
         :states  {:idle    {:on {:start {:target :running}}}
                   :running {}}}))
    

re-frame.machines/machine-transition

  • Kind: function (owned by re-frame.machines — not a re-frame.core facade export)
  • Signature:
    (re-frame.machines/machine-transition definition snapshot event)
     {:status :ok    :snapshot next-snapshot :fx [effect ]}
    | {:status :error :error {:kind error-id }}
    
  • Description: The pure transition fn. Given a machine definition, a current snapshot, and an event, returns one plain map — the shape Spec 005 §Level 1 settles.
    • :status :ok carries the new :snapshot and the ordered effects vector :fx; an event no transition matches is :ok with the snapshot unchanged and :fx [].
    • :status :error is the engine's failed macrostep — a guard / action / :data fn threw (:kind :rf.error/machine-action-exception, with :exception and the throwing ref) or a bounded-depth limit tripped (:kind :rf.error/machine-always-depth-exceeded / :rf.error/machine-raise-depth-exceeded). No snapshot rides a failure; the macrostep is atomic.
    • Programmer-input errors (a malformed :state, a dangling guard / action ref) throw the same :rf.error/* ex-info the registration validators throw — they are not results.
    • JVM-runnable; no live frame needed. re-frame.machines is the only namespace a caller requires.
  • Worked example — drive a transition and assert on the snapshot:
    (require '[re-frame.machines :as rf.machines])
    
    (let [{:keys [status snapshot fx]}
          (rf.machines/machine-transition login-flow
                                       {:state :idle :data {}}
                                       [:auth.login/submit {:email "a@b.com" :password "secret"}])]
      (is (= :ok status))
      (is (= :submitting (:state snapshot)))
      (is (= :rf.http/managed (ffirst fx))))   ;; the :submitting :entry fired the request
    

Inspection and queries

There is no per-kind query accessor on this namespace — neither machines nor machine-meta (both retired, rf2-kuky.31). A machine is an :event registration carrying :rf/machine? true, so both questions are answered by the one {id meta} registrar grammar every tool already speaks.

  • Enumerating registered machines: filter the generic registrar read on the :rf/machine? discriminator.
    ;; Every registered machine-id (the registry, not a frame's live snapshots).
    (keys (into {} (filter (fn [[_ m]] (:rf/machine? m)))
                (rf/registrations {:source :store :kind :event})))
    ;; → (:session :auth.login/flow …)
    
    This enumerates registered TYPES. A spawned actor carries no per-instance registrar entry, so live instances are read from the runtime-db snapshots map instead — see 005 §Querying machines.
  • Reading ONE machine's spec: its registered spec — transition table, :doc, :schemas, per-element source-coords — reads back through the same generic registrar query plus the documented :rf/machine inner-key projection:
    ;; The registered spec back out — table, doc, schemas, source-coords.
    (:rf/machine (rf/handler-meta {:source :store :kind :event :id :session}))
    ;; …or read just the declared :data schema:
    (get-in (rf/handler-meta {:source :store :kind :event :id :session})
            [:rf/machine :schemas :data])
    
    The projection is nil unless that :event registration is a machine. See API.md §Public registrar query API.

Keyword surfaces

The framework-registered subscription vectors and reserved effect tuples that address machines by keyword. These are unioned into every resolved image generation, since a :select-ns image cannot reach them by namespace. An image-loaded frame therefore resolves them the same way a default frame does.

[:rf/machine machine-id]

  • Kind: subscription (framework-registered)
  • Signature:
    [:rf/machine machine-id]
    
  • Description: The canonical machine read. Returns a reaction whose value is the snapshot {:state :data} (plus framework-managed :tags), or nil if the machine is not yet initialised. The table walks through subscribing to a snapshot and chaining named projections off it.
  • Example:
    (let [{:keys [state data]} @(rf/subscribe [:rf/machine :auth.login/flow])]
      [:div "State: " (name state)])
    

[:rf.machine/has-tag? machine-id tag]

  • Kind: subscription (framework-registered)
  • Signature:
    [:rf.machine/has-tag? machine-id tag]
    
  • Description: Returns true iff the :tags set in the named machine's current snapshot contains tag. Returns false otherwise, including for unknown or not-yet-initialised machines. This is a derived sub: it reads the snapshot's containment-bit directly rather than chaining off :rf/machine. A view that only cares about one tag therefore re-renders only when that bit flips.
  • Example:
    @(rf/subscribe [:rf.machine/has-tag? :auth.login/flow :auth/busy])  ;; => true / false
    

[:rf.machine/spawn spawn-spec]

  • Kind: effect (reserved fx-id)
  • Signature:
    [:rf.machine/spawn spawn-spec]
    
  • Description: Spawn a dynamic actor instance. Emitted from any event handler's :fx (including machine actions and the declarative :spawn desugar). spawn-spec carries exactly one of :machine-id (the registered machine type to instantiate) or :definition (an inline spec map), plus optional keys:
    • :data — overrides the type's initial :data.
    • :id-prefix — actor ids are the deterministic <prefix>#<n> from a per-type counter. The prefix defaults to :machine-id. Ids are never gensym'd.
    • :fixed-actor-id — an explicit actor address; skips allocation. Use it when the spawner must hold the child's address: choose a fresh keyword, store it in ordinary :data, and pass it here.
    • :start — a single event vector dispatched to the new actor as [<spawned-id> <start>]. When absent, the runtime dispatches the synthetic [<spawned-id> [:rf.machine.spawn/spawned]].
    • Declarative :spawn state nodes accept the same keys plus :on-done / :on-error / :timeout / :on-timeout. On the declarative path, the framework binds the child's allocated id into the parent's :data at [:rf/spawned <invoke-id>].
    • Fails closed when :machine-id names an unregistered type and no :definition is supplied (:rf.error/machine-spawn-unregistered-type). Backed by spawn-fx.
  • Example:
    (rf/reg-event :session/start-logger
      (fn [_ _]
        {:fx [[:rf.machine/spawn
               {:machine-id     :machines/log-shipper
                :fixed-actor-id :logger      ;; a well-known address the app holds
                :data           {:buffer []}
                :start          [:logger/connect]}]]}))
    
    ;; Address the actor by the id you chose.
    (rf/reg-event :session/flush-logs
      (fn [_ _]
        {:fx [[:dispatch [:logger [:logger/flush]]]]}))
    

[:rf.machine/destroy actor-id]

  • Kind: effect (reserved fx-id)
  • Signature:
    [:rf.machine/destroy actor-id]
    
  • Description: Tear down an actor. Symmetric counterpart to :rf.machine/spawn; backed by destroy-machine-fx.
    • Runs the actor's :exit cascade and cancels its armed :after timers. Dissociates [:rf.runtime/machines :snapshots <actor-id>] (in runtime-db). Unregisters its event handler when one is registered.
    • A spawned actor has no per-instance registration — its liveness is its snapshot's presence.
    • Silent-idempotent: destroying an already-destroyed actor is a no-op.
  • Example:
    (rf/reg-event :session/stop-logger
      (fn [_ _]
        {:fx [[:rf.machine/destroy :logger]]}))
    

Final states and :on-done. Completion is finality: a child reports back by entering a :final? leaf, whatever spawn form its parent used, and dispatches nothing to its parent. Leaf states marked :final? auto-destroy the machine on entry. The parent (if any) receives :on-done with the child's :data slot, and the completion event then flows into the parent's ordinary macrostep, so the parent can also advance on it (:always, or an explicit :on {:rf.machine.spawn/done …}). So a spawn-shaped sub-process completes, the parent receives the result through :on-done, and the framework destroys the child. No manual :rf.machine/destroy is needed.

State-node key What it does
:final? Marks a leaf state as terminal. Entering it auto-destroys the machine. Capability axis :fsm/final-states.
:error? Requires :final?. Marks that terminal a failure — the parent's :spawn :on-error transition fires instead of :on-done; under a :spawn-all join it counts as a failed child.
:output-key Requires :final?. Designates the child's :data slot reported back via the parent's :on-done.
:on-done (spawn-spec key) (fn [{:keys [data result]}] new-data) on the parent's :spawn map, or on a :spawn-all child spec. Fires when the spawned child enters a non-error :final? state — applied at the parent's handler boundary on its next macrostep, not inside the child's teardown cascade. result is the child's :data slot named by the final state's :output-key (or nil).

See Final states in The table.

[:rf.machine/update-snapshot patch]

  • Kind: effect (reserved fx-id)
  • Signature:
    [:rf.machine/update-snapshot {:rf/machine-id <id> :rf/patch {:data {...}}}]
    
  • Description: Snapshot-level escape hatch. Emit from a callback's (or any event handler's) :fx vector to touch a machine's :state / :meta / :data atomically. The :data patch is gated by validate-update-snapshot-data! against the actor's [:schemas :data] schema before the fx writes it. The escape hatch is therefore not exempt from the :where :machine-data boundary. Per Spec 005 §Snapshot-level escape hatch.
  • Example:
    {:fx [[:rf.machine/update-snapshot {:rf/machine-id :session
                                        :rf/patch      {:data {:retries 0}}}]]}
    

[:raise event-vec]

  • Kind: effect (reserved fx-id, machine-only)
  • Signature:
    [:raise event-vec]
    
  • Description: Machine-only. Inside a machine action's :fx, routes the event back into the same machine atomically and pre-commit. Unbound outside machine actions.
  • Example:
    {:actions {:kick (fn [_] {:fx [[:raise [:tick]]]})}}
    

Cross-machine messaging

There is one send: [:dispatch [<actor-id> <event>]]. A machine IS an event handler, so the id you hold is the address you send to — there is no separate name registry.

When a child spawns declaratively under a parent, the framework binds the child's allocated id into the parent's :data at [:rf/spawned <invoke-id>], so the parent reads the address off its own snapshot. A hand-emitted spawn has no declarative invoke-id, so the spawner picks a fresh keyword address, passes it as :fixed-actor-id, and stores it in ordinary :data.

Machine-tooling exports (JVM)

The shipped machine-tooling exports are re-frame.machines aliases over re-frame.machines.tooling. The aliases are JVM-only; there is no re-frame.core facade export. CLJS tool consumers call re-frame.machines.tooling/<name> directly. The CLJS facade deliberately omits the tooling require, so an app that attaches no tool DCEs the tooling body. There is no framework-level machine->xstate-json, machine->mermaid, or Stately bridge. Those exporters are owned by the separate post-v1 day8/re-frame2-machines-viz library, not the framework.

The machine algebra views Xray and re-frame-pair navigate are not among them. The static view over a machine definition and the live view over a machine instance ship no public accessor (Derivations §Machines expose algebra views): they live in re-frame.machines.tooling and every consumer names that namespace directly — Xray statically, re-frame.derivation.graph through requiring-resolve on the JVM.

re-frame.machines/machine-selector?

  • Kind: function (owned by re-frame.machines, JVM-only — not a re-frame.core facade export)
  • Signature:
    (re-frame.machines/machine-selector? sub-id)  boolean
    
  • Description: True iff the subscription registered under sub-id is a machine selector: an ordinary reg-sub whose literal :inputs include a [:rf/machine …] (or [:rf.machine/has-tag? …]) query vector. Machine selectors stay ordinary ephemeral :derivation subscription nodes, not a second subscription system. This recognizer lets a graph tool flag the ones that read a machine. JVM-only.
  • Example:
    (rf.machines/machine-selector? :session/summary)  ;; => true / false
    

re-frame.machines/machine-selector-targets

  • Kind: function (owned by re-frame.machines, JVM-only — not a re-frame.core facade export)
  • Signature:
    (re-frame.machines/machine-selector-targets sub-id)  #{machine-id }
    
  • Description: The set of machine ids the subscription registered under sub-id reads as a machine selector. Each id is the second element of an accepted [:rf/machine machine-id …] / [:rf.machine/has-tag? machine-id …] literal :inputs entry. Where machine-selector? answers only the boolean, this returns the actual target machine ids a graph tool needs to draw the edge. JVM-only.
  • Example:
    (rf.machines/machine-selector-targets :session/summary)  ;; => #{:session}
    

Implementation-tier effect handlers

The fx handlers behind the reserved :rf.machine/* effect ids. This namespace registers them via reg-fx, so an app that doesn't pull in day8/re-frame2-machines carries neither the trace strings nor the handler symbols on its production-elision bundle. App code emits the effect tuple (Keyword surfaces, above) — it does not call these fns directly. Each takes the standard (handler fx-ctx args) shape. The cascade-envelope frame is the fx-context :frame.

re-frame.machines/spawn-fx

  • Kind: function (owned by re-frame.machines, implementation tier — the fx handler for :rf.machine/spawn)
  • Signature:
    (re-frame.machines/spawn-fx fx-ctx spawn-spec)
    
  • Description: Installs the spawned actor's snapshot at [:rf.runtime/machines :snapshots <spawned-id>] in the spawning frame's runtime-db. It stamps the revertible :rf/machine-type TYPE reference so the lazy resolver and epoch restore can rebuild the actor. The actor's liveness IS that snapshot's presence; there is no per-instance event-handler registration. Fails closed when :machine-id names an unregistered machine type and the spawn carries no inline :definition: it emits the always-on :rf.error/machine-spawn-unregistered-type and installs nothing.

re-frame.machines/spawn-all-init-fx

  • Kind: function (owned by re-frame.machines, implementation tier — the fx handler for :rf.machine/spawn-all-init)
  • Signature:
    (re-frame.machines/spawn-all-init-fx fx-ctx args)
    
  • Description: On entry to a :spawn-all-bearing state, the runtime emits this fx alongside the per-child :rf.machine/spawn fxs. It seeds the join state at [:rf.runtime/machines :spawned <parent> <invoke-id>] with the shape {:children {…} :done #{} :failed #{} :resolved? false :spec …}. Each child's finality — a :final? leaf, or an :error? true one — folds into that state and resolves the join; children dispatch nothing to the parent themselves. Machine-internal — not for direct application use.

re-frame.machines/destroy-machine-fx

  • Kind: function (owned by re-frame.machines, implementation tier — the fx handler for :rf.machine/destroy)
  • Signature:
    (re-frame.machines/destroy-machine-fx fx-ctx args)
    
  • Description: Picks the teardown path from the args shape: the keyword-form / single-:spawn teardown, or the :spawn-all children-iteration teardown. It runs the actor's :exit cascade, clears its [:rf.runtime/machines :snapshots <actor-id>] slot, and drops its event-handler registration.

re-frame.machines/after-schedule-fx

  • Kind: function (owned by re-frame.machines, implementation tier — the fx handler for :rf.machine/after-schedule)
  • Signature:
    (re-frame.machines/after-schedule-fx fx-ctx args)
    
  • Description: On entry to an :after-bearing state node, the runtime emits one of these per :after entry. It resolves the delay (a literal pos-int?, a subscription vector, or a (fn [snapshot] ms)) and schedules a real wall-clock timer via the clock abstraction. For subscription delays it also installs an add-watch that cancels and reschedules when the sub's value changes. The synthetic expiry event is [<parent-id> [:rf.machine.timer/after-elapsed <delay-key> <epoch> <decl-path>]]. It fires only when the scheduling node is still active and the carried epoch matches. Machine-internal — not for direct application use.

re-frame.machines/after-cancel-fx

  • Kind: function (owned by re-frame.machines, implementation tier — the fx handler for :rf.machine/after-cancel)
  • Signature:
    (re-frame.machines/after-cancel-fx fx-ctx args)
    
  • Description: Cancels a previously-scheduled :after timer for a machine state. Machine-internal — not for direct application use.

Validators

The registration-time and :data-schema-boundary validators. The three :data validators live inside a (when interop/debug-enabled? …) gate, so production builds (goog.DEBUG=false) skip them and return true.

re-frame.machines/validate-machine!

  • Kind: function (owned by re-frame.machines, implementation tier — the pure registration-time grammar validator)
  • Signature:
    (re-frame.machines/validate-machine! machine)
    
  • Description: Runs every registration-time check the machine grammar requires.
    • Covers history-state placement, the closed key-set, the at-most-one-per-compound rule, :default-target resolution, :type :parallel region shape, and top-level dispatch plus guard/action ref resolution.
    • Composed at the top of make-machine-handler so the registered handler fn's body is exclusively request processing.
    • Throws the :rf.error/machine-* taxonomy on a grammar violation (e.g. :rf.error/machine-history-misplaced / -history-extra-keys / -history-duplicate / -history-bad-default-target, :rf.error/machine-unknown-node-key, :rf.error/machine-unresolved-guard / -unresolved-action).
    • The conformance corpus's :reg-machine Mode-B op pins the registration-error taxonomy against this leaf fn.

re-frame.machines/validate-machine-data!

  • Kind: function (owned by re-frame.machines, implementation tier)
  • Signature:
    (re-frame.machines/validate-machine-data! runtime-db event-id frame-id)  boolean
    
  • Description: Walks every snapshot under [:rf.runtime/machines :snapshots] in runtime-db and validates its :data against the resolved machine's [:schemas :data] schema.
    • Returns true iff every snapshot conformed, or carried no schema / no validator. Returns false on the first failure, with the per-snapshot trace already emitted. The router then rolls back the whole transition — the same mechanism as the :where :app-db rollback.
    • Schema resolution covers a SINGLETON (via the :rf/machine registrar projection) AND a SPAWNED actor (via the snapshot's :rf/machine-type).
    • This is the post-commit boundary the router AND-conjoins with validate-app-schema!.

re-frame.machines/validate-spawn-data!

  • Kind: function (owned by re-frame.machines, implementation tier)
  • Signature:
    (re-frame.machines/validate-spawn-data! spawned-id spec snapshot)  boolean
    
  • Description: Sibling of validate-machine-data! for the :rf.machine/spawn install path. Validates a freshly-built initial snapshot's :data against the spawned actor's machine [:schemas :data] schema BEFORE the snapshot lands in runtime-db. Returns true on conform, no schema, or no validator. Returns false on failure, and the caller skips the install. A spawn failure does not commit, so there is nothing to roll back (:phase :spawn emits with :rollback? false).

re-frame.machines/validate-update-snapshot-data!

  • Kind: function (owned by re-frame.machines, implementation tier)
  • Signature:
    (re-frame.machines/validate-update-snapshot-data! machine-id merged-snapshot)  boolean
    
  • Description: Sibling validator for the :rf.machine/update-snapshot escape-hatch fx. Validates the would-be-merged snapshot's :data against the actor's resolved [:schemas :data] schema BEFORE the fx writes the patch into runtime-db. Returns true on conform, no schema, or no validator; the fx proceeds with the write. Returns false on failure; the fx SKIPS the write so the invalid :data never installs. The escape hatch is therefore not exempt from the :where :machine-data boundary.

Runtime and lifecycle helpers

re-frame.machines/install-machine-runtime!

  • Kind: function (owned by re-frame.machines, implementation tier)
  • Signature:
    (re-frame.machines/install-machine-runtime!)
    
  • Description: Re-registers the machine runtime effects and subs into BOTH the regular registrar AND the framework-standard registry. An image-loaded frame can therefore resolve [:rf.machine/spawn …] / [:rf/machine …] through its sealed generation. Idempotent.
    • Re-registers from descriptors captured at ns-load, so it works even after a registrar/clear-all! has wiped the registrar slots. It is the machine analogue of the :rf/set-db standard re-seed.
    • Called at ns load, from the :machines/install-runtime! late-bind hook the reset fixture fires, and directly by tests that wipe the registrar.

re-frame.machines/reset-timers!

  • Kind: function (owned by re-frame.machines, implementation tier)
  • Signature:
    (re-frame.machines/reset-timers!)
    (re-frame.machines/reset-timers! frame-id)
    
  • Description: Cancel in-flight :after timers.
    • The 0-arity form clears every frame's timers. This is the fixture-teardown shape used by re-frame.test-support's reset-runtime and per-feature artefact test fixtures.
    • The 1-arity form clears just the given frame's timers. This is the frame/destroy-frame! hook shape: it releases a destroyed frame's host-clock handles and subscription watchers without touching siblings.
    • Spawn-id counters reset automatically with the registrar snapshot/restore + frame reset, so this hook handles only the frame-scoped wall-clock timer table.

re-frame.machines/owning-actor-id

  • Kind: function (owned by re-frame.machines, implementation tier)
  • Signature:
    (re-frame.machines/owning-actor-id frame-id event-id)  actor-id (or nil)
    
  • Description: Resolve the spawned-actor-id that OWNS event-id in frame-id, or nil.
    • Returns event-id (a keyword — the spawned actor's machine address) when a SPAWNED actor's snapshot is currently installed at [:rf.runtime/machines :snapshots <event-id>]. Otherwise returns nil: the event came from an ordinary handler or a singleton machine.
    • Set semantics are snapshot membership via the durable :rf/machine-type-at-root discriminator. This covers declarative :spawn / :spawn-all actors AND imperative [:rf.machine/spawn …] actors.
    • Published as the :machines/owning-actor-id late-bind hook. The http artefact uses it to ask "who owns this request's originating event?" (to abort managed HTTP on actor-destroy) without statically requiring this artefact. http falls back to nil when machines is absent.

See also

  • re-frame.core.mdreg-machine / defmachine are reached on the re-frame.core facade; dispatch / subscribe / reg-event drive and read a machine. A tag read is the [:rf.machine/has-tag? <machine-id> <tag>] subscription — a subscription vector, not a fn.
  • re-frame.schemas.md — machines declare schemas for their :data slot the same way ordinary handlers do; the validate-*-data! validators gate them.
  • The table — the flat-table contract: guards, actions, encapsulation, finals, schemas. The numbered Machines pages grow one login machine through tags, automatic transitions, hierarchy, parallel regions, history, and actors.
  • Glossary — the surface vocabulary in one place.
  • Coming from XState — the v6 parity delta for XState users.