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:
- Where it runs: locally on the user's machine vs. remotely on the Apify platform.
- 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 start → run --remote --detach
apify call → run --remote --wait
apify task run → run --remote --wait (Task target)
apify run → run --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.
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:
Today these axes are spread across differently-named commands instead of being expressed uniformly:
apify runapify actors startapify call(=apify actors call)apify task runapify actors build/apify pushWhy this is messy
apify call --detachorapify actors start --wait(though that's in preparation).apify run --remoteorapify call --local. And the two top-level shortcuts make it worse:runandcallpoint at opposite worlds.callvstask runuse different verbs for "execute this on the platform and wait," differing only in Actor vs Task target.--helpdoesn'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
runcommand (or a shared internal executor)) that takes the behavior as explicit options instead of encoding it in the verb: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 start→run --remote --detachapify call→run --remote --waitapify task run→run --remote --wait(Task target)apify run→run --local --waitThis 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
--local/--remoteand--wait/--detachper entry point (aliases pin their own defaults).buildfit, same executor or a sibling? It shares the same wait/no-wait axis, not the remote/local axis.