Skip to content

Reconcile synchronous-await vs fire-and-forget (background) behavior across Job commands (run / start / call / task run / build) #1213

Description

@DaveHanns

Summary

Our "Job" commands (run, start, call, task run, actors start, ...) encode two behavioral axes in their command names rather than in consistent verbs + flags. The result is an inconsistent, undocumented surface that's hard to reason about and hard to teach.

This issue proposes we (1) document the current behavior explicitly, then (2) draft a reconciliation around a single execution command with options, mapping the existing commands onto it as backward-compatible aliases.

The axes of behavior

There are really only two independent choices a user makes:

  1. Where it runs: locally on the user's machine vs. remotely on the Apify platform.
  2. Whether the command waits: block synchronously until the job reaches a terminal state, vs. fire-and-forget (return as soon as the job is created / queued).

Today these axes are spread across differently-named commands instead of being expressed uniformly:

Command Where Waits for finish? Streams logs? Notes
apify run local yes (it is the local process) yes (stdout) dev loop; spawns a child process
apify actors start cloud no — returns immediately no true fire-and-forget
apify call (= apify actors call) cloud yes, explicitly (polls to terminal) yes
apify task run cloud yes yes runs a remote saved Task
apify actors build / apify push cloud waits for the build to finish yes

Why this is messy

  • The wait/no-wait choice is a command name, not a flag. Nothing about the word "call" signals "this blocks"; nothing about "start" signals "this returns immediately." You switch commands to switch behavior — there is no apify call --detach or apify actors start --wait (though that's in preparation).
  • The local/remote choice is also a command name, not a flag. Nothing about the word "run" signals "this executes locally"; nothing about "call" or "start" signals "this executes on the platform." You switch commands to switch where it runs — there is no apify run --remote or apify call --local. And the two top-level shortcuts make it worse: run and call point at opposite worlds.
  • Inconsistent verbs for the same operation. call vs task run use different verbs for "execute this on the platform and wait," differing only in Actor vs Task target.
  • The wait semantics aren't documented. --help doesn't tell you whether a command blocks or returns immediately - you simply have to know. It's neither a flag nor spelled out anywhere. In cases it is documented, it is in plain english and non standard framing, making it hard to discover for agents.

Proposed direction (to be refined in discussion)

Introduce a single execution model (e.g. a run command (or a shared internal executor)) that takes the behavior as explicit options instead of encoding it in the verb:

apify run [target]
  --local | --remote        # where (default: ?)
  --wait   | --detach       # sync vs fire-and-forget (default: ?)
  --json                    # structured output
  --output-dataset          # etc.

Then map the existing commands onto it as thin, backward-compatible aliases so we don't break anyone's muscle memory or scripts:

  • apify actors startrun --remote --detach
  • apify callrun --remote --wait
  • apify task runrun --remote --wait (Task target)
  • apify runrun --local --wait

This keeps all current invocations working (alias → canonical), gives us one place to implement wait/exit-code/output logic, and lets us document a single coherent mental model. We don't need to deprecate anything initially, just unify the implementation and surface the flags.

Scope / open questions

  • Confirm and pin the exact semantics of each command as a test matrix before refactoring.
  • Decide sensible defaults for --local/--remote and --wait/--detach per entry point (aliases pin their own defaults).
  • Where does build fit, same executor or a sibling? It shares the same wait/no-wait axis, not the remote/local axis.

Metadata

Metadata

Assignees

No one assigned

    Labels

    t-dxIssues owned by the DX team.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions