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.
TaskLifecycleTrait.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 examplebazel …via thetools/bazelwrapper, or your repo’s own custom developer CLI (e.g.mycorp build …instead ofaspect build …) when you’ve wrapped Bazel/Aspect behind a company-branded entry point. - Drop the vanilla-
bazelalternate suggestion that built-ins tack on by default — useful when your team always has the Aspect CLI on PATH. - Add a description to a suggestion that ships without one, so the rendered line carries a
# <label>above the command explaining what it does.
Requires Aspect CLI v2026.23.18 or newer.
Hook signature
.aspect/config.axl
Verdicts
Return one of three values from your hook:| Verdict | Effect |
|---|---|
REPRO_FIX_ACCEPT | Keep the suggestion unchanged. Chain continues to the next hook. |
REPRO_FIX_REJECT | Drop the suggestion entirely. Short-circuits the chain — no further hook sees it. |
repro_fix_replace(command=, description=) | Rewrite the command and/or description. Omit either field to keep the prior value. Chain continues with the rewritten entry. |
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:
| Field | Type | Notes |
|---|---|---|
task_path | str | Joined <group...> <name> CLI path, e.g. "lint", "dev test-repro-commands". |
task_name | str | Just the task name (e.g. "lint", "buildifier"). |
task_group | list[str] | The group prefix (e.g. [], ["dev"]). |
kind | str | Task-result kind — "lint_results", "bazel_results", "format_results", "gazelle_results", "delivery_results". Matches TaskUpdate.kind so hooks can route by the same key the surface templates use. |
command_kind | ReproFixCommandKind | "repro" for entries from 🔁 Reproduce, "fix" for entries from 🛠️ Fix. |
command | str | The suggestion’s shell-ready command string. |
description | str | Optional short label rendered as a # <description> comment above the command. Empty when the task emits a single suggestion of its kind. |
slug | str | Stable kebab-case identifier — the recommended scoping key. See suggestion slug catalogue below. |
status | str | Lifecycle status the task is about to emit: "running", "failing", "passed", "warning", "failed", "aborted". |
exit_code | int | Task exit code. Only meaningful on the terminal emit; 0 mid-task. |
bazel_subcommand | str | "build", "test", or "run" for Bazel-wrapping tasks; "" otherwise. |
targets | list[str] | Resolved target patterns the task ran on. |
failed_targets | list[str] | Deduplicated list of Bazel labels that failed. Empty on non-Bazel tasks. |
extras | dict | Open-ended, task-stamped, curated keys. Documented per-task. Examples: lint stamps {"aspects": [...], "suggestion_count": N}; format and gazelle stamp {"affected_files": [...]}; delivery stamps {"mode": str, "targets_total": N}. |
data | dict | Escape hatch — full task data dict for hooks that need fields not surfaced by the typed fields or extras. Treat as unstable; prefer the typed surfaces above. |
Suggestion slug catalogue
Every suggestion carries a stable kebab-caseslug so hooks scope by suggestion identity instead of parsing the command string. The full catalogue (as of v2026.23.18):
| Task | Slugs |
|---|---|
lint | lint-repro, lint-fix |
format | format-repro, format-fix, format-fix-truncated, format-fix-rediscover, format-fix-vanilla-bazel |
gazelle | gazelle-repro, gazelle-fix, gazelle-fix-truncated, gazelle-fix-rediscover, gazelle-fix-vanilla-bazel |
delivery | delivery-repro-local-preview |
bazel (build / test) | bazel-repro-aspect, bazel-repro-vanilla-bazel |
bazel alternate suggestion ends in -vanilla-bazel, so a single hook can suffix-match to scope across all producers without listing each slug.
Examples
Three production-grade examples, all currently shipping in theaspect-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 vanillabazel 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
endswith("-vanilla-bazel") suffix match catches every current producer (format, gazelle, build, test) and any new ones 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 eachreplace 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 on the original slug and short-circuit before the rewrite runs (or worse, on a rewritten slug the next hook can no longer recognize).
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.
How it fits into the lifecycle
apply_repro_fix_hooks runs automatically on every surface emit via the framework’s dispatch_task_update — no surface (CLI printer, Buildkite annotation, GitHub Status Check body, PR-comment rollup) ever renders an un-hooked entry. An idempotence flag on each ReproFixCommand makes repeated dispatches safe; each entry runs through the chain at most once even though every emit dispatches.
Hooks see the same suggestions every consumer sees, in the same order, so a rewrite or rejection flows through to every surface in lock-step.
See also
- How to run and define tasks — task fundamentals and the
task()definition surface. - Aspect Extension Language overview — what AXL is and why it’s typed Starlark.
tools/bazelwrapper — pairs naturally with the rewrite-to-bazelhook in the first example.

