Durable Dispatch Protocol

Copy Markdown View Source

Squid Mesh's new runtime path treats dispatch state as an append-only journal. The protocol and pure projection are storage-independent, and SquidMesh.Runtime.Journal persists the same entries through Jido.Storage thread journals and checkpoints.

Threads

  • Run thread: workflow lifecycle facts such as run start, planned runnables, applied runnable results, and terminal status.
  • Dispatch thread: runnable intent, claim, heartbeat, completion, failure, retry visibility, and live wakeup facts.
  • Run index thread: rebuildable lookup entries for finding runs by workflow or host-facing keys.

SquidMesh.Runtime.Journal maps those logical threads to Jido thread IDs such as squid_mesh:run:<run-id>, squid_mesh:dispatch:<queue>, and squid_mesh:run_index:<workflow>. Runtime entries keep the Squid Mesh protocol type as the Jido entry kind and store the protocol data as the entry payload, so projections can be rebuilt from the thread after process restart.

Jido.Storage Boundary

The journal boundary accepts any configured Jido.Storage adapter. It appends entries with Jido's optimistic :expected_rev option, returns {:error, :conflict} for stale appends, and stores projection checkpoints with the exact Jido thread revision they cover. Checkpoints are rebuild accelerators; the append-only thread remains the source of truth.

This first storage-backed slice proves the Squid Mesh protocol can persist and restore dispatch projections through Jido.Storage. The live runtime still uses the current host-executor and Postgres table path until the Jido-native workflow and dispatch agents land.

For production adapters, the required storage properties are:

  • ordered thread append with stable per-thread sequence numbers
  • optimistic append conflict detection through :expected_rev
  • checkpoint overwrite semantics for compact projections
  • durable reload of thread entries and checkpoints after process restart

The Postgres path should use a jido_ecto adapter when that adapter provides those properties. The Bedrock path should use a jido_bedrock adapter where Bedrock is available. Squid Mesh should not introduce a second persistence contract for those stores; adapters only need to satisfy Jido.Storage.

Commit Order

Durable facts must be appended before live effects are treated as successful. A worker wakeup is recoverable only when a matching runnable intent already exists in the dispatch journal. If a wakeup is lost after the intent append, the projection can still rediscover the visible attempt after restart. Duplicate runnable intent entries are idempotent when their scheduled fields match; conflicting entries for the same runnable_key are anomalies.

For dependency-based workflows, Runic-ready runnables map to durable runnable intent. Independent root steps may produce sibling runnable intents for the same run, and a join step produces intent only after every dependency result has already become durable. The dispatch protocol does not use host-job concurrency as the source of truth for fan-out or fan-in readiness; persisted workflow facts do.

IntentLedger Alignment

Squid Mesh models workflow-specific facts, but its dispatch vocabulary is compatible with IntentLedger's durable intent model:

  • attempt_scheduled maps to a visible intent.
  • runnable_key is the Squid workflow identity for an intent.
  • step and input map to intent kind and payload.
  • claim_id, claim_token_hash, owner_id, and lease_until mirror IntentLedger claim fencing and lease state.
  • attempt_heartbeat, attempt_completed, and attempt_failed require the current claim fence.

Squid Mesh should integrate through a dispatch backend adapter rather than make IntentLedger depend on Squid-specific workflow concepts. The adapter can map Squid runnables to IntentLedger intents and translate lifecycle signals back into the projection.

Job Runner Boundary

The protocol does not assume Oban, Broadway, IntentLedger, or a custom process as the delivery mechanism. A runner may wake, claim, execute, retry, or redeliver work, but Squid Mesh treats the journal as authoritative for intent, claim, lease, fencing, completion, failure, and retry visibility.

Claims, Leases, And Heartbeats

Each attempt is fenced by claim_id and claim_token_hash. Workers hold the raw claim token, but durable entries record only its hash. A heartbeat extends lease_until only when it carries the current claim fence. Heartbeats from stale claims are ignored by the projection and surfaced as anomalies. Expired claims remain discoverable so a dispatch agent can redeliver work without relying on in-memory state. A replacement claim is valid only after the prior lease has expired; active claim takeover is an anomaly. Claims are valid only after the attempt's visible_at, and heartbeat, completion, and failure facts are valid only before the current lease expires.

IntentLedger is the intended future integration point for heartbeat execution and lease management once its durable Ecto/Postgres path is stable. Until then, Squid Mesh keeps the protocol dependency-free.

Completion And Retry

Completion and failure entries must also carry the current claim fence. Duplicate completion entries with the same claim and result are idempotent. Conflicting or stale completions are ignored and reported as anomalies. Retry scheduling is a durable fact with its own visible_at, so retry visibility survives restart. A runnable result can be applied to the run thread only after the matching completion is durable.

Terminal Runs

A run_terminal entry fences remaining dispatch work for the run. Rebuilt projections exclude terminal-run attempts from visible and expired-claim redelivery views, and later wakeup, claim, completion, failure, or apply entries for that run are surfaced as anomalies.