Skip to content

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, loop are valid. An unknown key is rejected with unknown key in workflow.stages.<NAME>: <KEY>; valid keys: agent, prompt, loop.
  • loop must 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.
  • agent is 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 agent concepts. The pipeline-file stage field agents (plural) names the roles that organize the work inside a stage — planning, implementation, review, summary. See Agent modes. The workflow-file stage field agent (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 prompt does not override or replace the stage's own prompt. The stage's declared prompt remains the authoritative instruction; the workflow prompt sits beside it as additional context.
  • The workflow prompt does 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:

  1. Find the step in the body whose name or id equals stage_name.
  2. If not found — silently skip with a warning (a workflow may cover multiple pipelines).
  3. If found:
  4. When workflow_stage.agent is not None — set the stage's command field to the composed wrapper path /home/goga/bin/<agent>-as-claude.sh.
  5. When workflow_stage.prompt is not None — attach the prompt as per-stage context for the agent running the stage. The prompt has lower precedence than the stage's own prompt field and is treated as ambient guidance, not as a direct command. See Workflow prompt — 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 formatdepends_on is 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