Feature

Writing workflows

A workflow is one Go function. Its sessions, loops, branches, and prompts are all visible where they happen.

A whole workflow

This is the built-in review workflow. Nothing is left out.

// Review asks one reviewer to inspect the repository read-only and record its findings.
func Review(ctx context.Context, env gimbal.Env, params ReviewParams) error {
	gimbal.Set(ctx, "goal", params.Goal)
	reviewer := gimbal.NewSession(ctx, gimbal.RoleCodeReview, env.WorkDir)
	result, err := reviewer.Generate[Result](ctx, reviewPrompt)
	if err != nil {
		return err
	}
	gimbal.SetJSON(ctx, "result", result)
	return nil
}

Record the goal in scope. Open a session for a role. Ask for a typed answer. Record the result. The prompt is a constant a few lines below; the goal reaches the agent because it was set in the scope the turn runs in.

A bigger one

The built-in implement workflow, condensed: each supplied outcome gets a planner loop; a coder does bounded tasks under a coach, an optional command gathers evidence, and an independent validator judges the outcome. The excerpt omits operational error handling and the task bound; the full source carries both.

for outcomeCtx, outcome := range gimbal.Iterate(ctx, "outcome", outcomes) {
	gimbal.Set(outcomeCtx, "outcome", outcome)
	planner := gimbal.NewSession(outcomeCtx, gimbal.RoleSprintPlanning, env.WorkDir)
	plannerCoach := gimbal.NewSession(outcomeCtx, gimbal.RoleArchitecturalCritique, env.WorkDir)
	loop := gimbal.PromiseLoop(outcomeCtx, "implementation", outcome, planner,
		gimbal.WithSupervisor(plannerCoach, scopePrompt))

	for taskCtx, task := range loop.Tasks {
		worker := gimbal.NewSession(taskCtx, roleCoding, env.WorkDir)
		workerCoach := gimbal.NewSession(taskCtx, gimbal.RoleArchitecturalCritique, env.WorkDir)
		workerReport, err := worker.Generate[gimbal.Text](taskCtx, implementationPrompt,
			gimbal.WithSupervisor(workerCoach, scopePrompt))
		// Record the report; if supplied, run task.Validation.Command.

		validator := gimbal.NewSession(taskCtx, gimbal.RoleQAOrchestration, env.WorkDir)
		assessment, err := validator.Generate[Assessment](taskCtx, validationPrompt)
		// Record the assessment; stop only when it finds no substantial gap.
		if assessment.ValidationPassed && len(assessment.SubstantialGaps) == 0 {
			break
		}
	}
	// Check loop.Err(), operational errors, and the per-outcome task bound.
}
a recorded run of the same shape
The console map of a planner loop with its planner selected.
The same shape as a run. Planner, task scope, watched coding turn, commands, validator, and the arrow back.

There is no workflow engine underneath. range, break, if, and return do what they do in any Go program. Retries, critique rounds, bake-offs, and delivery steps are code you write inline, because a tactic hidden in a helper is a tactic nobody can read.

What keeps it readable

  • Prompts are compile-time constants or embedded files. What an agent is sent is readable at the call site, never assembled somewhere else. Task data travels as scoped context.
  • Workflows name roles, never models. gimbal.RoleCodeReview says what kind of thinking the step needs. A run binds it.
  • Answers are typed. Generate[Result] sends the struct’s JSON Schema to the harness and validates what comes back. Field comments are instructions to the agent.
  • Lifetimes follow context.Context. Returning from a scope closes its sessions and stops its services. Cancelling the run interrupts the agents.
  • gimbal lint catches authoring mistakes statically, standalone or as a go vet tool.

From function to command

go generate turns the entry function into a CLI command and a graph:

  • one flag per field of the parameter struct, with the field’s comment as help
  • one model flag per role the workflow names, defaulting from one shared file
  • the function’s doc synopsis as short help, the package doc as long help
  • a typed workflow.Graph literal, read from the function’s source, that the console draws

Review the workflow while editing its source

The workflow design pages merged on Gimbal main show each built-in workflow as a selectable diagram, alongside a usage guide and a generated run form. Selecting a step reveals its prompt, scoped context, and Go source. The selected step, open branches, detail panels, and camera position can be kept in a URL for a precise conversation with another person or an agent.

In development, just dev watches relevant Go source and refreshes the diagram, guide, and parameter descriptions after an edit. An invalid source edit leaves the previous view visible with an error. This is a design-time view of source, not the recorded state of a running workflow. PR #436 records browser checks of the edit-and-refresh loop; the latest tagged release checked on October 9 predates its merge.

Choosing a shape

NeedShape
One well-scoped taskOne agent turn.
Dependent stagesA sequence, with explicit results passed onward.
Independent investigationsConcurrent branches in a Group, then a join.
Several agents need the same researchResearch once, then Fork the conversation.
A known finite collectionIterate over the items.
The next task depends on new evidenceA PromiseLoop with worker and validator feedback.

Use the smallest shape that does the job.

For the agreed target behavior of service readiness, shutdown, confidential values, and consumer admission, see the secrets and orchestration contract. It separates design promises from implementation evidence.

Next Secrets & orchestration