Skip to main content
Built-in tasks (build, test, lint, format, gazelle, delivery) emit two kinds of follow-up suggestions when something noteworthy happens:
  • Repro commands: “how do I reproduce this failure?” Rendered with a 🔁 Reproduce: header.
  • Fix commands: “how do I apply the suggested fix?” Rendered with a 🛠️ Fix: header.
The same suggestions appear everywhere a task’s outcome is rendered: the CLI terminal output, Buildkite annotations, GitHub Status Check bodies, and the aggregate PR task summary comment. Phases.repro_fix_suggestion lets your .aspect/config.axl accept, reject, or rewrite each suggestion before any surface renders it. Common uses:
  • Rewrite suggested aspect … commands to your team’s preferred form, for example bazel … via the tools/bazel wrapper, or your repo’s own custom developer CLI (e.g. mycorp build … instead of aspect build …) when you’ve wrapped Bazel/Aspect behind a company-branded entry point.
  • Drop the vanilla bazel alternate suggestion for one task but not another.
  • Add a description to a suggestion that ships without one, so the rendered line carries a # <label> above the command explaining what it does.
To drop a whole flavor without a hook, pass --repro-flavor or --fix-flavor (repeatable, aspect and/or bazel, default both). build, test, lint, format, and gazelle take --repro-flavor; lint, format, and gazelle also take --fix-flavor. Set them from config the same way as any task arg:
.aspect/config.axl
Use a hook when the decision depends on more than the flavor.

Hook signature

.aspect/config.axl
Hooks are invoked in registration order on every repro/fix entry, on every surface emit. Each entry runs through the chain at most once, so it’s safe to register hooks that intermix with other features that also touch repro/fix data.

Verdicts

Return one of these from your hook: A replace with both command and description left as None is a no-op (equivalent to accept).

ReproFixInfo fields

The single info argument carries everything a hook typically needs to decide:

Suggestion slug catalog

Every suggestion carries a stable kebab-case slug so hooks scope by suggestion identity instead of parsing the command string. The full catalog: Convention: every vanilla-bazel alternate suggestion ends in -vanilla-bazel, so a single hook can suffix-match to scope across all producers without listing each slug.

Examples

Some of these examples come from the aspect-build/aspect-cli repo’s own .aspect/config.axl.

Rewrite aspect … to bazel … for one task

Useful when your repo has a tools/bazel wrapper and you want suggestions to use the bazel form so new contributors copy-paste a command that goes through the checked-in Bazel version.
.aspect/config.axl

Drop the vanilla-bazel alternate everywhere except one task

Built-in tasks tack on a vanilla bazel run <target> suggestion next to their aspect … repros/fixes, a fallback for environments where the Aspect CLI isn’t on PATH. If your team always has aspect (or a tools/bazel wrapper that routes through it), those alternates are noise.
.aspect/config.axl
The endswith("-vanilla-bazel") suffix match catches every producer, including any added later.

Rewrite a description without touching the command

repro_fix_replace takes either field independently. Passing only description keeps the existing command unchanged.
.aspect/config.axl

Order matters

Hooks run in registration order, and each replace verdict feeds the next hook in the chain. So if you register a rewrite hook and a reject hook, register the rewrite first. Otherwise the reject may match the original entry and short-circuit before the rewrite runs. The slug is preserved across replace verdicts (hooks rewrite commands and descriptions, not suggestion identity), so slug-scoped hooks downstream of a replace still match correctly. But the rewritten command / description is what downstream hooks see.

See also