3.7 Workflow Orchestration (Outside the Graph)
Bazel's contract for a command like bazel test //... is to inspect the graph
implied by the request, then produce declared outputs and test results under
Bazel's output tree. That contract is what makes Bazel incremental and
cacheable. Repository work that needs a different output boundary does not fit
an ordinary BUILD target.
Repeated workflows do not all belong in the build graph. A release archive of
the whole checkout, a bazel query ... selection piped into bazel test, a
Gazelle rewrite, a coverage report uploaded to CI, and a
bazel run //docs:publish step have different contracts. Some are graph
requests. Some are side effects. Some are scripts around Bazel. Their inputs,
outputs, and side effects determine which layer should run them.
Bazel Is The Engine, Not Every Workflow
The sequence starts with 3.7.1 Bazel's Output Boundary because the boundary has to be visible before the patterns make sense. Bazel owns graph inspection and declared outputs. The workspace symlinks, source tree, deployment target, CI artifact store, and comparison against another revision sit outside that core contract. Naming that boundary shows whether a task belongs in a rule or in an outer workflow.
3.7.2 Workflows Outside Bazel then turns the boundary into day-to-day maintainer judgment. Whole-repo fan-in, two-snapshot checks, source-tree rewrites, publish flows, and coverage reporting still use Bazel, but they usually need a wrapper step, a bazel run verb, or a task runner around the Bazel invocation. Let Bazel do the graph work and put the orchestration around it.
3.7.3 Aspect Extension Language (AXL) is the optional scaling answer. If the outer workflow is still one shell line, keep it one shell line. If it becomes a real program that coordinates multiple Bazel calls, listens to build events, or drives repo-wide tools, AXL gives that layer a Starlark shape without pretending it is a BUILD rule.
Request Shape Determines The Layer
The organizing idea is the shape of the request. A normal Bazel request is rooted in targets: build this target, test this target set, inspect this graph. Workflow orchestration starts when the request is instead rooted in an operation: rewrite files, compare this commit with another commit, collect outputs from unrelated targets, publish a result, or turn raw coverage into a report.
That distinction connects this section back to 2.2 Three Phases of a Build. Bazel's loading, analysis, and execution phases work best when the output contract is declared before actions run. A task that mutates the checkout or decides what to do after reading CI artifacts does not fit that contract cleanly. It may still call Bazel, and often should, but it should not be disguised as an ordinary build action.
It also separates this section from 3.5 Developer Experience & Local Tooling. Developer-experience tooling asks how people invoke Bazel comfortably: IDE sync, wrappers, plugins, local tools. Workflow orchestration asks a stricter architectural question: where does this repeated job live relative to Bazel's graph and output boundary?
Collector Targets Reveal Misplaced Orchestration
Collector targets often exist only so another workflow can treat the repository
as one artifact. A giant collector target, a whole-repo glob(["**/*"]), or a
rule that writes back into the source tree usually means orchestration is being
forced into the build graph.
That does not make every side effect wrong. It means the side effect should be explicit. bazel build and bazel test stay inside the declared-output model. bazel run can cross the boundary on purpose. A script or AXL task can ask Bazel for labels, outputs, or build events, then do the part that Bazel should not model as a cached action.
Decide: A release task builds a declared archive, compares it with another revision, and uploads it to a registry. Which parts belong in Bazel's target graph, and which belong in an outer workflow?
Reveal
Producing the archive from declared inputs belongs in Bazel because it is a cacheable target output. Fetching or comparing another revision and uploading to a registry are procedures with external state or side effects, so a script, CI job, or task runner should orchestrate them. The outer workflow can call Bazel for the artifact and graph facts without pretending the whole release is one build action.
Workflow orchestration starts where Bazel's declared-output contract ends. Keep graph inspection and artifact production in Bazel. Put source-tree writes, cross-revision comparison, repo-wide fan-in, reporting, and publishing in an explicit outer layer, with a clear boundary between Bazel and the scripts or tasks around it.
Sections in this chapter · 3
Bazel inspects graphs and populates bazel-out — everything else needs external orchestration.
Common patterns that need external orchestration — archives, collectors, version comparison, coverage.
Starlark **task programs** that chain Bazel invocations and external tools — complements Aspect CLI (3.5.4), which extends the developer-facing Bazel command line.