Summary
Today dotflow start --step module.func runs a single @action step. To execute a workflow composed of multiple task.add(...) calls you have to wrap everything inside an @action-decorated function that calls .start() itself — which mixes step and orchestrator semantics and makes the CLI unable to control the execution mode.
This RFC proposes a new, explicit entry point: --workflow.
Motivation
Current pain — projects like dotflow-cloud end up with:
@action
def main():
workflow = DotFlow(config=Config())
workflow.task.add(step=step_one)
workflow.task.add(step=step_two)
workflow.start(mode="sequential")
return workflow
Problems:
@action semantically means one step; here it hides a whole
orchestrator
.start() is hardcoded inside; the CLI can't override mode,
storage, etc.
--step resolves this as a step and would execute it as one task,
not as a pipeline
Proposal
Add --workflow (or -w) to the start command. It resolves a factory module.attr that returns a DotFlow instance, and the CLI is responsible for starting it.
Usage
dotflow start --workflow dotflow_cloud.workflow.main
dotflow start --workflow dotflow_cloud.workflow.main --mode parallel
Factory shape
from dotflow import Config, DotFlow
from dotflow_cloud.actions import step_one, step_two
def main() -> DotFlow:
workflow = DotFlow(config=Config())
workflow.task.add(step=step_one)
workflow.task.add(step=step_two)
return workflow # no @action, no .start() here
CLI behavior
--step and --workflow become mutually exclusive (one required)
- With
--workflow: import factory → call it → assert it returned a DotFlow → call workflow.start(mode=self.params.mode)
--mode, --storage, --path continue to work (CLI wins over factory defaults when explicitly passed)
Proposed changes
dotflow/cli/setup.py — add --workflow in a mutually exclusive group with --step
dotflow/cli/commands/start.py — branch on params.workflow, new private _start_from_factory() using Module(self.params.workflow)
dotflow/core/exception.py — new InvalidWorkflowFactory raised when the resolved attr is not callable or does not return DotFlow
- Docs + example in
docs_src/ showing the factory pattern
Open questions
- Override semantics — should CLI flags (
--mode, --storage) always override factory config, or only when the factory did not set them? Recommendation: CLI always wins
- Dotted attribute support —
Module already supports pkg.module.attr via rpartition, so no change there
- Schedule parity — should
dotflow schedule --workflow ... get the same treatment?
- Return-type validation — should we allow factories that return
None and call .start() themselves (backwards compat), or strictly require a returned DotFlow?
Non-goals
- No change to
@action semantics
- No deprecation of
--step — it remains the way to run a single task
Summary
Today
dotflow start --step module.funcruns a single@actionstep. To execute a workflow composed of multipletask.add(...)calls you have to wrap everything inside an@action-decorated function that calls.start()itself — which mixes step and orchestrator semantics and makes the CLI unable to control the execution mode.This RFC proposes a new, explicit entry point:
--workflow.Motivation
Current pain — projects like
dotflow-cloudend up with:Problems:
@actionsemantically means one step; here it hides a wholeorchestrator
.start()is hardcoded inside; the CLI can't override mode,storage, etc.
--stepresolves this as a step and would execute it as one task,not as a pipeline
Proposal
Add
--workflow(or-w) to thestartcommand. It resolves a factorymodule.attrthat returns aDotFlowinstance, and the CLI is responsible for starting it.Usage
Factory shape
CLI behavior
--stepand--workflowbecome mutually exclusive (one required)--workflow: import factory → call it → assert it returned aDotFlow→ callworkflow.start(mode=self.params.mode)--mode,--storage,--pathcontinue to work (CLI wins over factory defaults when explicitly passed)Proposed changes
dotflow/cli/setup.py— add--workflowin a mutually exclusive group with--stepdotflow/cli/commands/start.py— branch onparams.workflow, new private_start_from_factory()usingModule(self.params.workflow)dotflow/core/exception.py— newInvalidWorkflowFactoryraised when the resolved attr is not callable or does not returnDotFlowdocs_src/showing the factory patternOpen questions
--mode,--storage) always override factory config, or only when the factory did not set them? Recommendation: CLI always winsModulealready supportspkg.module.attrviarpartition, so no change theredotflow schedule --workflow ...get the same treatment?Noneand call.start()themselves (backwards compat), or strictly require a returnedDotFlow?Non-goals
@actionsemantics--step— it remains the way to run a single task