Skip to content

Decision: retire user_input's $input pause mechanism — needs real pause/resume ports instead #95

Description

@jhamill34

Decision record

runners/user_input's InputPrompter/$input mechanism is retired conceptually: "user input" isn't the right abstraction for this engine going forward. The concept we actually want — pausing and resuming a workflow across execution calls — needs a properly designed port, not this one.

Why

The current mechanism (runners/user_input::UserInput, backed by execution_engine's InputPrompter trait and the "$input" special-cased identifier in Engine::run) works by blocking the calling thread on an mpsc channel for up to 60 seconds, keyed by execution ID in an in-memory Signals map (Arc<Mutex<HashMap<...>>>) shared between the engine and apid's gRPC provide_input handler.

That design conflates two different things:

  • Prompting a human for input mid-run.
  • Suspending an execution's state so it can be resumed later — potentially much later, across process restarts, not just within a 60-second blocking window on a live thread.

Everything about the current implementation (thread-blocking wait, in-memory-only state, hardcoded timeout) is shaped around the first framing and doesn't hold up for the second, which is the one we actually care about for workflow_engine-style pause/resume (see #68, #69, #70, and the sibling lua_runner retirement in #73). Better to design real pause/resume ports as part of that effort than keep investing in or routing new work through $input.

Not in scope here

This is a decision record only — no code changes yet. runners/user_input, the InputPrompter trait, Engine::run's "$input" handling, apid's provide_input RPC, and apicli provide-input all keep working as-is for now.

Plan (once real pause/resume ports exist)

  1. Design the actual pause/resume port(s) needed for workflow execution — likely something that can checkpoint/serialize state rather than block a thread, aligned with wherever workflow_engine's async dispatch path lands.
  2. Once that exists and nothing new is being authored against $input, do a full strip — not just an unwire:
    • execution_engine: remove the InputPrompter trait, the input_handler field, register_input, and the "$input" special case in Engine::run.
    • apid: remove user_input's Cargo dependency, its input feature, the construct_execution_engine wiring, the Signals type threaded through ApiDaemon, and the provide_input RPC.
    • apicli: remove the provide-input command.
    • runners/user_input itself can stay on disk for reference, same treatment as python_runner/javascript_runner (Decision: python_runner/javascript_runner are permanently legacy — no new investment #72) — just no longer wired into anything.
  3. python_runner's task.continueAfterUserInput binding (runners/python_runner/src/bindings.rs) calls engine.run("$input", ...) — once the strip happens, that call becomes dead code hitting a nonexistent op. That's fine: python_runner is already reference-only per Decision: python_runner/javascript_runner are permanently legacy — no new investment #72, same treatment as the LUA arm left unreachable per Decision: retire runners/lua_runner once workflow_engine's async dispatch path lands #73.

Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions