Operation Coordination#
Services coordinate by passing Operations through the knowledge graph and firing an event trigger. This page describes how a caller dispatches work, how a receiver pulls and executes it, and how the system ensures durability and consistency without synchronous RPC. The graph is the source of truth for operation state; the per-resource queue is an in-memory cache reconstructed at startup.
Terms this page assumes
Operation · Capability · Workflow · Service · Resource · ClassScope
Capability Resolution#
An Operation never references a Workflow directly. Resolution always follows a two-hop chain through
a Capability. The Operation individual links to a Capability via cfc:implementsCapability. The
Capability links to exactly one Workflow via svc:realizedByWorkflow. This indirection ensures
that callers address a capability rather than a specific function implementation. Planning services
discover work against Capabilities; the middleware resolves the concrete Workflow at execution time.
Dispatching Work#
Caller-side dispatch is a transactional context manager. The caller uses the request context
manager to create the Operation in the graph and notify the receiver atomically. The body of the
context manager populates the Operation properties. The atomic exit performs the graph write and
fires the receiver’s event trigger. This guarantees that an Operation is never created without
notification, and notification is never sent for an uncommitted Operation.
with mw.request(target_resource) as op:
op.status = "queued"
op.implementsCapability = capability_iri
# Additional domain properties set here
# Exit commits the Operation and triggers the receiver
The Event Trigger#
Every resource-mode middleware exposes a built-in execute() Workflow on its REST API. The dispatch
process fires this trigger after committing the Operation. The trigger carries only the Operation
IRI; the payload lives entirely in the graph. The trigger returns immediately and does not block
on the work. It does not return a business result. Callers must not expect a synchronous response.
The trigger simply enqueues the Operation for later processing by the receiver’s domain code.
Operation Queue#
Each resource maintains an in-memory queue of Operations addressed to it. This queue is durable
because the Operation and its status are written to the graph before the trigger fires. On startup,
a middleware reconstructs its queue by querying the graph for its own queued Operations. It also
reclaims any orphaned running Operations left from a previous session. A missed trigger loses no
work; the baseline is self-healing. Resources do not poll the graph continuously for new work.
Status Lifecycle#
An Operation moves through three states: queued, running, and terminal (done or failed).
Execution provenance is written as part of the terminal transition. The provenance records which
Workflow ran the Operation, when it ran, and the result. The status field itself serves as the
provenance record; there is no separate success flag. A done status implies success; failed
implies an exception or validation error during execution. Transitioning to a terminal state is
atomic and irreversible without dispatching a fresh Operation.
Pull-and-Run#
Receiver-side execution uses the claim_next transactional context manager. The domain code takes
the next queued Operation, sets it running, and runs the work in the body. The middleware
re-fetches the Operation under a domain-supplied ClassScope to ensure the correct data view.
On atomic exit, the context manager records the terminal done or failed state. If the body
raises an exception, the transaction reverts and the status updates to failed with provenance.
with mw.claim_next(scope=view_scope) as claimed:
# Read the Operation's domain fields off `claimed.operation`
# (re-fetched under the ClassScope you passed to claim_next).
op = claimed.operation
result = perform_work(op) # physical actuation, calculations, etc.
# Assign a str to `claimed.result` to attach a result. On clean exit the
# middleware records it as `executionResult`, alongside `executedByWorkflow`
# and `executionTimestamp`.
claimed.result = result
# Exit records done/failed and dumps state on failure
Inside the block, claimed.operation is the read side — the Operation materialized under your ClassScope — and claimed.result is the write side. Setting claimed.result is optional; a body that only actuates need not set it.
Handover Primitive#
Bilateral coordination between participants uses the mw.handover primitive rather than a plain
dispatch. Handover models a timeless administrative possession switch preceded by a timed physical
preparation phase. The primitive is a context manager that checks preconditions in __enter__ and
commits the switch in __exit__. It operates on Core’s reified cfc:PossessionState. The switch
is a single atomic transaction that updates the Workpiece’s cfc:hasPossessedWorkpiece link.
with mw.handover(mode="Put", workpiece=wp, counterpart=other):
# Physical transport and preparation
# Coordinate with counterpart if needed
# Exit atomically switches possession state
The handover primitive ensures that the “possessed by exactly one” cardinality constraint is never transiently violated. The old PossessionState is kept as implicit history. The middleware does not model the physical transport itself; that logic belongs in the context manager body.
Failure Recovery#
If a resource dies mid-Operation, its stranded running Operations are handled centrally. A
watchdog-mode instance sweeps stale resources and transitions their orphaned running Operations
to failed. The failed status carries any dumped resource state available at the time of failure.
Recovery is never a silent physical replay. The planner or domain decides whether to dispatch a
fresh Operation. Physical operations are frequently not safely repeatable, so automatic re-actuation
is avoided. The watchdog ensures the graph does not retain stale running states indefinitely.