Invocation Runtime Context¶
Pynenc uses thread-local runtime context to answer a simple question while a task is running: “what execution environment am I currently inside?”
That runtime context is stored in pynenc.context, but the domain objects live
near the systems they describe. For example, RunnerContext lives under
pynenc.runner, while pynenc.context only stores and retrieves the active
runner context.
Active Contexts¶
Pynenc currently tracks several runtime contexts:
Context |
What it represents |
Storage owner |
|---|---|---|
Runner context |
The runner, process, and thread executing work |
|
Distributed invocation context |
The invocation currently running in this thread |
|
Trigger event context |
The trigger event currently being evaluated or emitted |
|
Current app |
The active Pynenc app for core services and nested calls |
|
pynenc.context is intentionally an ambient registry. It should not become the
home for domain classes; it should only expose get/set/swap helpers for active
runtime state.
Invocation Kinds¶
Pynenc distinguishes the kind of invocation being executed:
Invocation kind |
Created by |
Workflow meaning |
|---|---|---|
|
calling a |
Defines a workflow identity |
|
calling an ordinary |
Standalone, or a child inside an existing workflow |
This distinction is reconstructed from the resolved task role:
WorkflowTask creates WorkflowInvocation, while ordinary Task creates
TaskInvocation. State backends persist workflow membership, parent
references, and event origin; they do not need a separate invocation-kind field.
Workflow Membership¶
A WorkflowInvocation always has a workflow identity. Its workflow_id is the
same value as the workflow-defining invocation id.
A TaskInvocation may have workflow membership when it is called from inside a
workflow. In that case it can use shared workflow APIs such as:
current_task.wf.identity
current_task.wf.get_data("status")
current_task.wf.set_data("status", "payment_authorized")
A top-level ordinary task is standalone. It does not pretend to be a workflow, and workflow accessors fail clearly when no workflow membership exists.
This is a runtime fact. Static typing can know that @app.workflow creates a
workflow-defining task, but it cannot know whether an ordinary @app.task call
will run standalone or inside an existing workflow.
Root-Only Workflow Operations¶
Only workflow-defining invocations may orchestrate workflow replay:
workflow_task.wf.root.uuid()
workflow_task.wf.root.random()
workflow_task.wf.root.utc_now()
workflow_task.wf.root.execute_task(child_task, *args, **kwargs)
Ordinary child tasks can share workflow data, but they cannot call root-only
operations. If they try, Pynenc raises DeterministicOperationScopeError with
debugging details such as the current task id, invocation id, workflow id, and
whether the invocation is workflow-defining.
The task.wf helper is always present. What changes at runtime is whether the
current invocation has workflow membership. Only WorkflowTask.wf exposes
.root statically.
This keeps the model clear:
workflow roots make deterministic orchestration decisions
ordinary tasks perform activity work
shared workflow data remains available to all members of the workflow
Distributed Coherence¶
Pynenc’s invocation state machine already ensures that one invocation attempt is
owned by one runner. PENDING exists to acquire ownership before execution, and
ownership is required for the transition into RUNNING.
The workflow layer therefore does not add another distributed lock for deterministic operations.
The coherent split is:
local runtime state: the deterministic operation cursor on the active workflow-defining invocation object
persisted state: deterministic values and child calls stored by workflow identity
orchestrator state: retry count, rerouting, and invocation ownership
If a workflow invocation is retried on another runner, that runner reconstructs the same invocation id and workflow identity, starts a fresh local cursor, and replays the values already persisted for that workflow id.
Execution Boundary¶
The current distributed invocation context is installed when a runner starts running an invocation. It is restored or cleared when the invocation returns, fails, or is rerouted.
runner owns invocation
-> install current app
-> install runner context
-> install current invocation context
-> run task function
-> restore previous contexts
For workflow replay behavior built on top of this invocation model, see Workflows.