Saga-blind — a SAGA orchestration runtime that executes business logic it has never seen

After years working on distributed systems under tight specification constraints, I find myself free to explore ideas purely out of curiosity — no deadlines, no stakeholders, no “we can’t do that in production.” This is the second incursion after saga-graph.

saga-blind is a runtime that accepts .saga definition files dropped into a watched folder and executes them without restarting. Steps live in a jar declared in the definition. The engine loads the jar at launch time, resolves parameter mappings from an OKV pool, and calls the step providers. It knows nothing about what the steps do.

saga: goblin-recruit
jar: ./libs/goblin-services.jar

steps:
  - id: measurements
    kind: mandatory
    class: sagablind.goblin.MeasurementsService
    inputs:
      - param: goblinId
        from: __init__/goblinId

  - parallel:
    - id: smithy
      kind: mandatory
      class: sagablind.goblin.SmithyService
      inputs:
        - param: armLength
          from: measurements/armLength
      compensate:
        - param: weaponId
          from: smithy/weaponId

  - id: enlist
    kind: mandatory
    class: sagablind.goblin.EnlistService
    inputs:
      - param: weaponType
        from: smithy/weaponType

Every state transition is written to a WAL before the action it describes. If the process dies mid-execution, a ZombieHunter agent reconstructs the pool and the jar from the WAL and retries forward — or compensates LIFO if the retry fails.

The jar contract is minimal: execute(args: Map[String, ujson.Value]) and compensate(args: Map[String, ujson.Value]). The jar receives named values and returns named outputs. It knows nothing about the pool, the WAL, or the orchestration around it.

Scala 3 · SQLite · Cask · no framework dependencies in core.

Source, architecture diagrams and demo videos: GitHub - ccerdadiaz/saga-blind · GitHub

Feedback welcome.