Skip to main content
Pick the workflow primitive from the shape of the work, not from habit:

TaskPipeline

TaskPipeline is a tick-driven runner for fixed sequences. Each named step returns a StepResult, and the pipeline advances only on SUCCESS.

Interaction bridges

Bridge InteractionResult into step results instead of translating statuses by hand:
  • StepResult.fromInteraction(result) turns SUCCESS into step success and everything else into FAILED.
  • StepResult.fromInteractionPaced(result) retries only PACED.
  • StepResult.fromInteractionTransient(result) retries the SDK default transient set: paced actions, temporarily missing or hidden targets and widgets, and bank, deposit-box, and production interfaces that have not opened yet.
  • StepResult.fromInteractionRetrying(result, statuses...) retries exactly the caller-provided statuses.

Yield instead of poll

When an action takes time to land (a bank booth click that loads an interface), dispatch once and yield until the event fires. This advances the pipeline exactly when the widget loads and skips redundant evaluations while waiting:

Diagnostics and shared context

  • currentStepTicks() counts ticks spent on the current step, including delay ticks.
  • currentStepAttempts() counts executions of the current step body.
  • Both reset when the pipeline advances, resets, or completes.
StepContext is the mutable context passed through builder-produced steps. Use put/get/contains/remove for cross-step scratch state such as a selected target or last observed count, and getLabels() with label(...)/jump(...) for flow control. Keep keys narrow to the workflow that owns them. TickDelay and Cooldowns provide deterministic named countdowns.

TypesafeCarouselStateMachine

TypesafeCarouselStateMachine<E> is the suite’s state-machine engine for branching and looping workflows. The builder requires a workflow ID, a lifecycle owner, an initial state, and exactly one handler per enum constant. Missing or duplicate handlers, a missing owner, or a missing initial state fail at build().
Call pulse(clientTick) at most once per game tick. A duplicate-tick pulse returns a stay result with reason duplicate_tick without executing the handler. Every handler returns one of:
  • CarouselResult.stay(reason, detail) keeps the current state.
  • transitionTo(target, reason, detail) moves to another state.
  • complete(reason, detail) records a successful terminal state.
  • fail(reason, detail) records a failed terminal state.
Use machine.getContext() for workflow-owned scratch values and tick deadlines, snapshot() for immutable status and transition history, cancel(reason, detail) for cooperative cancellation, and reset() before reusing a terminal machine. To hand a machine to a managed runtime, wrap it with AutomationLoop.fromStateMachine(config, machine). AutomationLoop keeps its own break handler gate and accepts a WorkflowSupervisor for cooperative guards that return RUN, SUSPEND, INTERRUPT, or CANCEL between pulses.

Observable runtime and telemetry

ObservableWorkflow is the read-only telemetry boundary. Active workflows publish immutable WorkflowSnapshot values through WorkflowRegistry, and terminal or failed workflows stay registered until you call reset() or stop() so the final decision remains visible. History is bounded to 64 decisions per workflow. The Agent Server exposes registered snapshots at GET /api/v1/workflow/status and through the n3_get_workflow_status MCP tool.

Runtime rules

  1. Observe live state before choosing a transition.
  2. Perform at most one meaningful action per tick.
  3. Inspect InteractionResult. An accepted or paced dispatch is not confirmation.
  4. Re-fetch entities and widgets after state-changing actions.
  5. Reset transient workflow state on logout, disable, or scene invalidation.
  6. Leave suite-wide pacing and walker ticking to Packet Utils.