Pipeline File
A pipeline-file is the base YAML document that defines a pipeline. It
carries a header (name, description, optional overrides) and a body
that lists the ordered stages of the pipeline.
Pipeline-files live at:
| Source | Path |
|---|---|
| project | <cwd>/.goga/pipelines/<name>.yml |
| user | ~/.goga/pipelines/<name>.yml |
Only top-level *.yml files are scanned. Subdirectories are ignored and
.yaml files are excluded. On name conflict, the project source wins.
Document shape
Every pipeline-file is a two-segment YAML document separated by a --- line:
<header>
---
<body>
- The header is a YAML mapping with
name,description, and an optionalrolesblock (see Roles). - The body is either a YAML list (phases format) or a YAML dict (stages format).
A file without the --- separator is rejected at parse time with a
structural error.
Header
name: GogaFeature
description: "Feature development"
roles: # optional block — see "Roles"
planner: |
Inline prompt that replaces the shipped default for the planner role.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Pipeline identifier, must be non-empty |
description |
string | yes | Short human-readable purpose |
roles |
map | no | Inline overrides for the three role prompts. See Roles. |
A header missing name or description is rejected with a structural
error.
Body formats
The body can be authored in one of two shapes — both are first-class.
Phases format (list)
A YAML list of step mappings. Each item is keyed by name; depends_on is
auto-generated by list position — the first step has no dependency, each
subsequent step depends on the previous one.
- name: propose
title: "Create the task from a user propose"
communication: true
prompt: |
Save the task file as `<git branch --show-current>.md`
skills:
- goga-propose
- name: task-review
title: "Review of the created task"
communication: true
prompt: |
Review the task `<git branch --show-current>.md`
skills:
- goga-review-task
When a workflow applies loop: N to a phases stage, the expanded copies
chain naturally via list position — the first copy inherits the original
position, subsequent copies depend on their predecessor, and the next
original step depends on the last expanded copy.
Stages format (dict)
A YAML mapping keyed by step id. Each value carries title and an optional
explicit depends_on plus any extra fields. depends_on is passed through
as-is, except that workflow loop expansion rewrites external references to
a base name to the last expanded id.
propose:
title: "Create the task from a user propose"
communication: true
prompt: |
Save the task file as `<git branch --show-current>.md`
skills:
- goga-propose
task-review:
title: "Review of the created task"
depends_on: [propose]
communication: true
prompt: |
Review the task `<git branch --show-current>.md`
skills:
- goga-review-task
Any other body shape (scalar, no separator, or an already-compiled flow-file
shape) raises unsupported body format.
Stage fields
Both body formats accept the same set of fields per stage. The compiler preserves unknown fields verbatim — only the canonical fields below have assigned semantics:
The authoring field for user-input stages is
communication. The compiler translates it to the afm output keyinteractive(which stays stable in the compiled flow-file). Authoringinteractivedirectly is rejected with a structural error — usecommunication.Symmetrically, the authoring field for the launch mode is
trigger. The compiler translatestrigger: manualto the afm output keyauto_run: false(the stage pauses when reached and runs only when launched manually). Authoringauto_rundirectly is rejected with a structural error — usetrigger: manual.
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | — (required, phases only) | Stage identifier. In phases format the item's name; in stages the map key. |
title |
string | — (optional, recommended) | Display label emitted as the compiled stage's name. |
communication |
bool | false | Whether the stage prompts for user input. Authors as communication; compiles to the afm interactive key. Authoring interactive directly is a structural error. |
prompt |
string | — | Stage-level prompt text; emitted as the compiled prompt field. |
skills |
list of strings | — | Skills the agent must apply at this stage. |
before_script |
string | — | Shell script run before the stage; compiles to the afm script_before field. See Script directives. |
script |
string | — | Shell script run as the stage body; compiles to the afm script field. Mutually exclusive with prompt and skills. See Script directives. |
after_script |
string | — | Shell script run after the stage; compiles to the afm script_after field. See Script directives. |
timeout |
string | — (no key emitted) | Timeout for the stage's script action (Go duration, e.g. 30m); compiles to the afm script_timeout field. Requires script in the same body; the value passes verbatim (the duration grammar is validated by afm at runtime). See Script directives. |
roles |
list of strings | autonomous mode when absent | Agent roles assigned to the stage. See Roles. |
trigger |
string (on_success | manual) |
on_success (no key emitted) |
Launch mode of the stage. trigger: manual compiles to the afm auto_run: false key (canonical slot right after auto_approve) — the stage pauses when reached and runs only when launched manually; trigger: on_success (or an absent key) emits no auto_run key. Authoring auto_run directly is a structural error. Valid in both body formats and in workflow extend bodies; a workflow stages block can force or cancel it per-stage via manual (see Workflows). |
depends_on |
list of strings | auto (phases) / none (stages) | Stage dependencies. |
Body step title field
The body step carries a title field that becomes the compiled stage's
display name. Use a short human-readable phrase — it is what end users see
in stage listings.
Script directives
A stage may carry shell scripts to run at fixed points relative to the agent's work. The three authoring fields are translated into the matching afm output keys at compile time:
| Author as | Compiles to | When it runs |
|---|---|---|
before_script |
script_before |
Before the stage's agent work. |
script |
script |
As the stage body itself. |
after_script |
script_after |
After the stage's agent work. |
timeout |
script_timeout |
Bounds the stage's script action. |
The authoring keys are consumed and never appear in the compiled flow-file —
only the translated script_* keys do. A multi-line script_before,
script, script_after, or script_timeout serializes as a YAML
block-literal (e.g. script: |); a single-line value stays a plain scalar.
The rule is uniform across all four slots.
script is mutually exclusive with prompt and skills: a stage runs
either an agent-driven prompt (prompt/skills) or a literal shell script
(script), not both. Authoring script together with prompt and/or
skills is a structural error. before_script and after_script are
compatible with both script and prompt/skills — they bracket the stage
regardless of how its body is defined.
A stage whose body carries script compiles with no agents key at all:
afm rejects agents combined with script, so the default auto agent is
not injected and an authored body roles value (see
Roles) is element-validated but not emitted — body roles has no
effect on a script stage's compiled output. Only script opens this
suppression: before_script/after_script alone keep the default
agents: [auto] injection. The rule is uniform across both body formats and
in workflow extend bodies.
timeout scopes to the script action: it requires script in the same body
(before_script/after_script do not open the directive — a timeout
without script is a structural error, as is a non-string value, including
YAML-null). The value passes verbatim with no duration-grammar validation on
the goga side — a malformed string fails in afm at runtime. The key is
emitted only when authored; authoring script_timeout directly is not
forbidden, but when both are authored the translated timeout value wins.
Valid in both body formats and in workflow extend bodies; loop-expanded
copies inherit it.
deploy:
title: Deploy
before_script: |
set -e
echo "building..."
script: |
make build
make deploy
after_script: echo "done"
timeout: 30m
Shipped pipelines
Goga ships six ready-to-use pipelines — bugfix, development, patch,
refinement, review, and sync — that cover the most common authoring
lifecycles. See
Shipped Pipelines for the per-pipeline walkthrough and how
project pipelines override the global ones.
Roles
The pipeline-file carries roles in two distinct places, and they
serve different purposes. A third agent concept exists in
workflow-files and
is intentionally different — see the comparison at the end of this
section.
| Where | Field | Type | Purpose |
|---|---|---|---|
| Header | roles |
map | Replace shipped default prompts for the three authorable roles. |
| Body stage | roles |
list of strings | Choose which roles run the stage (its mode). |
| Workflow-file stage | agent |
string | Choose which CLI agent runs the stage (e.g. codex, claude). |
The rest of this section covers the first two — both live in the pipeline-file. The third is documented in Workflows.
The agent roles
When a stage runs in coordinated mode (see below), the roles in its
roles list execute in sequence — each role's output becomes the next
role's input. You author the three overridable roles by alias; the
compiler maps each alias to the afm agent name (and the matching shipped
prompt file):
| Author as (alias) | afm agent / prompt stem | Responsibility |
|---|---|---|
planner |
planning |
Read the task, decompose it into a verifiable plan, hand off. Does not execute the work. |
executor |
implementation |
Execute the plan task-by-task, including any required research, code, or analysis. Produces the deliverable. |
reviewer |
review |
Verify the deliverable against the plan and acceptance criteria. Approves or requests changes. |
A fourth afm role, summary, produces the final report for the run,
covering every stage. It is not an authorable role: it never appears
as a key in the header roles block, and its prompt file (summary.md)
is always materialized from the shipped default. (summary may still be
listed in a stage's roles field — like any non-alias value it passes
through to the afm agents list verbatim — but it is never an override
target.)
What each role does
planner (planning) decomposes the incoming task into an actionable
plan with three sections: a numbered task list, the assumptions made along
the way, and explicit acceptance criteria the work must meet. It does not
perform the work itself — it hands the plan to the next role. It makes
decisions autonomously rather than asking questions, and documents every
non-obvious choice under assumptions.
executor (implementation) executes the plan top to bottom. For
each task it decides the nature of the work — research, analysis, code,
discussion — and acts accordingly. It runs the acceptance criteria checks
before declaring the stage complete, and produces an honest report (not a
false "done") when a criterion cannot be met.
reviewer (review) audits the deliverable against the plan: every
task actually done, every acceptance criterion met, the result matching
the intent of the prompt, edge cases handled. It returns a single verdict
— approved or needs_changes — with a list of critical blockers and a
list of non-blocking suggestions.
summary reads the logs of every stage in the run and produces a
final report: a one-paragraph overview, a per-stage breakdown of what
happened, and any open issues carried over from review. Because it is not
an authorable role, its prompt is always the shipped default.
Body stage roles — choosing the stage mode
The per-stage roles field is a list of role aliases. A stage runs in one
of two modes depending on whether the list is non-empty:
- Autonomous mode —
rolesis absent,null, or an empty list[]. The stage receives its task and decides for itself how to organize the work: which steps to take, in which order, and when the result is complete. Use this for stages that should not be decomposed ahead of time — research, free-form authoring, exploratory analysis. - Coordinated mode —
rolesis a non-empty list (e.g.[planner, executor]). The listed roles run in sequence and organize the work for the incoming task: theplannerdecomposes the task into a verifiable plan, theexecutorexecutes that plan, thereviewervalidates the result, and thesummaryrole produces the final report. Use this for stages where you want predictable, reviewable execution — feature builds, bug fixes, anything with acceptance criteria.
The two modes are mutually exclusive per stage — there is no hybrid.
Authoring even a single-element list (roles: [planner]) is enough to
switch the stage into coordinated mode; an authored non-empty value
always wins.
Choosing between modes
- Leave
rolesout for stages whose work cannot be planned ahead of time — exploratory research, free-form authoring, asking a question. - Author
rolesfor stages whose work is structurally predictable — building a feature, fixing a bug, anything where you want a plan, execution, review, and a final report.
Examples
Autonomous mode — no roles field. The stage receives the prompt and
organizes the work itself:
- name: research
title: "Explore the codebase for prior art"
prompt: |
Find any existing implementations of feature flagging
in this codebase and summarize what you find.
Coordinated mode — planner and executor roles assigned. The stage
decomposes the task into a plan, then executes it:
- name: ship
title: "Build and ship artifacts"
roles:
- planner
- executor
prompt: |
Build the artifacts for the current branch and ship them.
Full lifecycle — all three authorable roles. Useful for stages where
you want end-to-end verification (the automatic summary report covers
the stage on top):
- name: deliver
title: "Deliver the feature end-to-end"
roles:
- planner
- executor
- reviewer
prompt: |
Implement the feature described in the task file, verify
it meets the acceptance criteria, and produce a report.
Header roles — overriding the default prompts
The header-level roles block lets you replace the shipped default prompt
for any of the three authorable roles with an inline prompt of your own.
An override fully replaces the default — it does not merge with it.
When the block is absent or empty, the three shipped defaults are used
unchanged. (summary has no override slot — its prompt is always the
default.)
The block accepts exactly three keys — one per authorable role:
| Key | Replaces shipped default prompt for afm agent |
|---|---|
planner |
planning |
executor |
implementation |
reviewer |
review |
Example:
name: GogaFeature
description: "Feature development"
roles:
planner: |
You are the planner for this feature pipeline.
Break the work into reviewable steps.
reviewer: |
Review each change against the feature's acceptance criteria.
---
Rules:
- Only those three keys are valid. An unknown key (including
summary) is rejected at compile time withunknown role in header.roles: <key>; valid keys: planner, executor, reviewer. - Each value must be a string. A non-string value raises
non-str value in header.roles.<key>. - The legacy
agentskey is rejected outright withagents key is forbidden in header; use roles. - An absent
rolesblock and an emptyroles:mapping are both treated identically — no overrides, the three shipped defaults are used unchanged. - Overrides are a pipeline-file-side artifact. They are not carried into the compiled flow-file — they are materialized into a separate prompts directory at run time.
How header and stage roles relate
The two pipeline-file roles declarations compose without collision:
- The per-stage
roleslist decides which roles execute and in which order — i.e. the stage's mode. - The header-level
rolesmap decides what each role's prompt says when it runs.
A stage running in coordinated mode picks up the header-level overrides
for whatever roles appear in its list. If no override is supplied for a
role, that role uses its shipped default. The header overrides do not
add roles to a stage — they only change the prompt of roles already listed
in the stage's roles field. A stage in autonomous mode (no roles
field) is unaffected by header overrides.
Pipeline-file roles vs workflow-file agent
| Aspect | Pipeline-file header roles |
Pipeline-file stage roles |
Workflow-file stage agent |
|---|---|---|---|
| Scope | Pipeline-wide default | Single stage | Single stage (overrides at run time) |
| Value | Map of role → prompt | List of role aliases | Single CLI agent name |
| Controls | The prompt text of each role | Which roles run, in which order (mode) | Which CLI binary runs the stage |
| Examples | {planner: "...", reviewer: "..."} |
[planner, executor] |
codex, claude, opencode |
Use pipeline-file declarations for the stable structure of a pipeline
(its roles and their prompts). Use workflow-file agent for per-project
variation — running the same pipeline on different CLI agents without
forking the pipeline-file. See Workflows for the full
workflow-agent semantics.
Errors
| Condition | Exception |
|---|---|
--- separator missing |
missing body separator |
Header missing name or description |
header missing name/description |
Legacy agents key in header |
agents key is forbidden in header; use roles |
Unknown key in header roles block (incl. summary) |
unknown role in header.roles: <key>; valid keys: planner, executor, reviewer |
Non-mapping roles block in header |
non-mapping roles block in header |
Non-string value in header roles.<key> |
non-str value in header.roles.<key> |
Legacy agents key in a stage body |
agents key is forbidden in stage body; use roles |
Authoring interactive in a stage body |
interactive key is forbidden in stage body; use communication |
Authoring auto_run in a stage body |
auto_run key is forbidden in stage body; use trigger: manual |
trigger value outside on_success/manual |
trigger must be one of: on_success, manual |
timeout value is not a string (including YAML-null) |
timeout must be a string in stage <NAME> |
timeout without script in the same body |
timeout requires script in stage <NAME> |
script authored together with prompt and/or skills |
script is mutually exclusive with prompt/skills in stage <NAME> |
| Body shape is neither list nor dict | unsupported body format |
| Body has zero steps | empty body |
See also
- Workflows — layer project-specific overrides on top of a compiled pipeline.
goga pipelineCLI reference — invocation flags, exit codes, and Docker mechanics.