Workflows
A workflow-file is an optional YAML document that layers project-specific
behavior on top of a compiled pipeline at run time. A workflow can inject a
top-level prompt, override the agent or prompt of specific stages, and expand
a stage into N chained copies via loop.
Workflow-files live at:
<cwd>/.goga/workflows/<name>.yml
They are project-only — there is no user-level workflow directory. The
name must be a bare filename resolved inside .goga/workflows/; path
traversal via .. or an absolute prefix is rejected.
Document shape
A workflow-file is a YAML mapping with up to two top-level keys:
prompt: |
Top-level prompt injected as the first directive of the compiled pipeline.
stages:
<stage-name>:
agent: codex # optional per-stage agent override
prompt: | # optional per-stage prompt override
Additional per-stage instruction.
loop: 2 # optional iteration count (>= 1)
| Key | Type | Required | Description |
|---|---|---|---|
prompt |
string | no* | Top-level prompt emitted as the first key of the output. |
stages |
map | no* | Per-stage override instructions keyed by stage name. |
* At least one of prompt or a non-empty stages block must be present;
an empty workflow is rejected with a structural error.
Unknown top-level keys are rejected with
unknown key in workflow: <KEY>; valid keys: prompt, stages.
Stage entries
Each entry under stages is keyed by stage name and accepts up to three
fields:
| Field | Type | Default | Description |
|---|---|---|---|
agent |
string | — | CLI agent name (e.g. codex, claude, opencode). Selects which agent runs this stage. See Workflow agent — choosing the CLI agent. |
prompt |
string | — | Per-stage context prompt. Lower precedence than the stage's own prompt — closer to a section description than to a direct instruction. See Workflow prompt — context, not command. |
loop |
int | — | Positive iteration count (>= 1). When >= 2, the stage is expanded into N chained copies. |
Rules:
- Only
agent,prompt,loopare valid. An unknown key is rejected withunknown key in workflow.stages.<NAME>: <KEY>; valid keys: agent, prompt, loop. loopmust be an int>= 1. Zero, negative values, and non-int types raise a structural error.- The stage value must be a mapping. Non-mapping values raise
non-mapping stage <NAME> in workflow.stages. - Stage names are not validated against any pipeline schema. A name that does not match any step in the target pipeline is silently skipped with a warning — a workflow may intentionally cover multiple pipelines.
agentis not validated against a known agent set. Absence of the corresponding wrapper file is surfaced at run time.
Workflow agent — choosing the CLI agent
Two different
agentconcepts. The pipeline-file stage fieldagents(plural) names the roles that organize the work inside a stage —planning,implementation,review,summary. See Agent modes. The workflow-file stage fieldagent(singular) is a different concept: it names the CLI agent that runs the stage —claude,codex,opencode, or any other installed wrapper. The two are orthogonal.
A workflow's per-stage agent field lets the same pipeline run different
stages on different CLI agents. This is what makes a pipeline portable
across CLI tools: you keep one pipeline-file (the work to be done) and
express "stage X should run on codex, stage Y on claude" in a
project-level workflow-file, without forking the pipeline.
Each agent value resolves to a wrapper script installed in the goga image
at /home/goga/bin/<agent>-as-claude.sh. The wrapper presents the named
CLI agent to the pipeline runner in a uniform invocation shape, so the
pipeline itself does not care which concrete CLI is underneath.
The canonical baseline wrappers shipped with the image:
agent value |
Wrapper |
|---|---|
claude |
claude-as-claude.sh |
codex |
codex-as-claude.sh |
opencode |
opencode-as-claude.sh |
Any other value is permitted as long as the corresponding wrapper file exists in the image — absence is surfaced at run time, not at validation time.
When a stage does not specify an agent in the workflow, the pipeline
uses its global default agent — typically claude. Setting agent
per-stage overrides that default for the named stage only; other stages
keep the global default.
Workflow prompt — context, not command
The per-stage prompt in a workflow-file is not a direct instruction
to the agent. Its weight is lower than the stage's own prompt field
declared in the pipeline-file, and it is
closer in role to a section description: it sets context, frames the
stage's intent, and gives the agent additional background to interpret the
stage's main prompt against.
Two consequences follow from this lower precedence:
- The workflow
promptdoes not override or replace the stage's ownprompt. The stage's declared prompt remains the authoritative instruction; the workflowpromptsits beside it as additional context. - The workflow
promptdoes not by itself command the agent to take specific actions. Free-form prose is interpreted as background, not as a directive.
To make a workflow prompt carry actual requirements, follow the
explicit labeled format the goga prompts already use — sections like
Requirements:, Constraints:, Algorithm:. Labeled blocks are
recognized as instructions and are honored as such; unstructured prose is
not.
Example — descriptive context only (no enforceable requirements):
stages:
propose:
prompt: |
This stage formalizes the user's request into a task document.
It runs early in the lifecycle and shapes the rest of the pipeline.
Example — context with enforceable requirements (the labeled block is honored as an instruction):
stages:
propose:
prompt: |
Task formalization process.
Requirements:
- Examine all link connections between cells carefully.
- Do not write code examples in the task.
Constraints:
- Do not build architecture in the task.
In the second form, the Requirements: and Constraints: blocks are the
load-bearing parts — the leading paragraph is still just context.
How the compiler applies a workflow
When compile_flow is invoked with a non-None WorkflowDocument, the
compiler reconstructs the parsed body in three deterministic passes before
building the output stages.
Pass 1 — Per-stage overrides (in-place)
For each (stage_name, workflow_stage) pair in workflow.stages:
- Find the step in the body whose
nameor id equalsstage_name. - If not found — silently skip with a warning (a workflow may cover multiple pipelines).
- If found:
- When
workflow_stage.agentis not None — set the stage'scommandfield to the composed wrapper path/home/goga/bin/<agent>-as-claude.sh. - When
workflow_stage.promptis not None — attach the prompt as per-stage context for the agent running the stage. The prompt has lower precedence than the stage's ownpromptfield and is treated as ambient guidance, not as a direct command. See Workflowprompt— context, not command.
Pass 2 — Loop expansion
For each step, determine loop_count from
workflow.stages[name].loop if present, else 1.
When loop_count >= 2, the compiler appends N copies with ids
<name>-1, ..., <name>-N, each subsequent copy depending on the previous
one. The compiler records an expanded_ids map from base-name to the list
of ids produced.
Expansion interacts with body format:
- Phases format — the expanded copies chain naturally via list position. The first copy inherits the original position; subsequent copies depend on their predecessor; the next original step depends on the last expanded copy.
- Stages format —
depends_onis otherwise passed through as-is. See Pass 3 for the external-reference rewrite.
Pass 3 — External depends_on rewrite (stages format only)
For each stage in stages format with a non-empty depends_on, the
compiler replaces any reference to a base name whose loop_count >= 2
with expanded_ids[ref][-1] — i.e. the last expanded id.
For example, if stage review has loop: 2 and another stage declares
depends_on: [review], the compiled output carries
depends_on: [review-2].
Pass 4 — Agent-mode resolution
After overrides and expansion, the compiler resolves the agent mode for
every stage the same way the no-workflow path does: a stage without an
authored non-empty agents value runs in autonomous mode, and a stage
with an authored non-empty agents value runs in coordinated mode. See
Agent modes for the functional description
of each mode.
A workflow-applied command override (from a per-stage agent field) and
the stage's own agent-mode resolution are independent — the override
selects which agent binary runs the stage, while the agents field
selects how the work is organized inside it.
Invocation modes
A pipeline run picks up a workflow in one of three mutually exclusive modes. The launcher communicates the chosen mode to the container through env-file entries.
| Mode | Invocation | Env-file entry | Behavior |
|---|---|---|---|
| Auto-match | goga pipeline deploy |
(neither) | If <cwd>/.goga/workflows/deploy.yml exists, it is applied silently. |
| Explicit override | goga pipeline deploy --workflow custom |
GOGA_WORKFLOW_NAME=custom |
Apply <cwd>/.goga/workflows/custom.yml. Host validates existence first. |
| Disable | goga pipeline deploy --no-workflow |
GOGA_WORKFLOW_DISABLED=1 |
Disable workflow application entirely. |
In auto-match mode no host-side validation runs — the workflow-file is opened and parsed inside the container, and a missing file is silently treated as "no workflow". In explicit-override mode the host validates the file exists before launch (exit 1 if missing).
--workflow and --no-workflow are mutually exclusive — passing both
exits with code 1 before launch.
Log line
When a workflow will actually be applied (explicit --workflow, or an
auto-match file that exists), the launcher prints a single line to stdout:
Pipeline running with workflow "<name>"
When no workflow applies, the launcher prints no workflow line.
Example
A workflow that layers a Russian-language answer directive and two
per-stage prompts on top of a feature pipeline:
prompt: |
Answer in Russian language
stages:
propose:
prompt: |
Task formalization process.
Requirements:
- When setting the task, it is necessary to develop stack technologies more carefully.
- Carefully examine all the link connections between cells
Constraints:
- Don't write code examples in the task
- Don't build architecture in the task
brainstorm:
prompt: |
Architectural design process.
Requirements:
- Annotations describe the high-level order of actions
- Every usage file is connected through Imports and referenced in annotations
- Usage files are self-contained
- Footer Description describes the responsibility zone
Constraints:
- Annotations must not reference previous functionality
- Annotations does not contains implementation details
- Annotations must not use "X from Imports" phrasing
- Footer Description does not contains details
A workflow that expands a propose-review stage into two passes and pins
its agent to claude:
stages:
propose-review:
loop: 2
agent: claude
Compiled effect: the original propose-review stage is replaced by
propose-review-1 and propose-review-2, each depending on the previous
one, both running with the claude wrapper.
A workflow that runs different stages on different CLI agents — authoring
on codex, review on claude — without touching the pipeline-file:
stages:
propose:
agent: codex
brainstorm:
agent: codex
architecture-review:
agent: claude
plan-review:
agent: claude
Here codex does the heavy authoring (propose, brainstorm) and claude
runs the reviews. The underlying pipeline-file stays unchanged — every
project can pin its own agent-per-stage matrix in its workflow-file.
Errors
| Condition | Exception |
|---|---|
| File is not valid YAML | invalid YAML in workflow-file |
| Root is not a mapping | workflow must be a mapping |
prompt present but not a string |
non-str value in workflow.prompt |
stages present but not a mapping |
non-mapping stages block in workflow |
| Unknown top-level key | unknown key in workflow: <KEY>; valid keys: prompt, stages |
| Stage value is not a mapping | non-mapping stage <NAME> in workflow.stages |
| Unknown per-stage key | unknown key in workflow.stages.<NAME>: <KEY>; valid keys: agent, prompt, loop |
agent present but not a string |
non-str value in workflow.stages.<NAME>.agent |
prompt present but not a string |
non-str value in workflow.stages.<NAME>.prompt |
loop present but not an int |
non-int value in workflow.stages.<NAME>.loop |
loop is an int but < 1 |
loop must be >= 1 in workflow.stages.<NAME> |
Neither prompt nor stages entries are present |
empty workflow — provide at least prompt or one stage |
See also
- Pipeline File — the base document a workflow layers on top of.
goga pipelineCLI reference — invocation flags for--workflow/--no-workflowand exit codes.