WARN lines or read docs to discover an improvement.
A tip has a severity (🔥 important / ⚠️ warning / 💡 suggestion / ℹ️ info), a title and body, and an identity that controls de-duplication and silencing. You add tips with add_tip from any code that has a TaskContext — most commonly a task_update hook in your .aspect/config.axl.
Adding a tip
Atask_update handler is the usual place to add a tip. The handler fires
repeatedly over a task’s lifetime, but that’s fine: re-emitting the same
(id, key) just replaces the stored tip (and the terminal only re-prints
when the content actually changed), so a static tip can emit on every
update without piling up or spamming:
.aspect/config.axl
add_tip is also a no-op when the Tips feature is disabled
(--tips:enabled=false), so emit sites never need to probe for it.
If building the tip’s content is expensive, gate the work behind a
once-flag. Keep the flag local to config and close over it — use a
one-element list so the closure can mutate it (AXL, like Starlark, makes a
plain assignment inside a nested function a new local, so a bare bool
wouldn’t stick):
.aspect/config.axl
add_tip arguments
Tip identity
A tip is identified by the pair(id, key):
idis the kind of tip and the silence key. Two emits with the same(id, key)are the same tip — the later one replaces the stored content (so a task can revise a tip it made earlier). Silencing is always byidalone, across everykey.keyis an optional discriminator for “same kind of tip, different subject.” For example, a flaky-test tip might useid = "flaky-test"withkey = "//pkg:my_test"so each flaky target gets its own tip, while--tips:silence=flaky-testsilences them all.
Severity ordering
Tips render most-urgent-first, stable within a bucket by insertion order. Severity is just a priority/visual signal — a tip can be about anything, not necessarily a failure or a feature:
Pick the level by how much you want it to stand out. Severity only
affects ordering and the emoji/label; silencing is by
id (see
Silencing tips), independent of severity.
Template types
Thetype argument selects how title / body are interpreted:
TIP_TEMPLATE_RAW(default) — no interpolation. Most tip bodies are literal markdown with{}that must survive verbatim, so this is the safe default.TIP_TEMPLATE_FORMAT— Starlarkstr.format(**vars);{name}placeholders are replaced fromvars.TIP_TEMPLATE_JINJA2— Jinja2 source rendered againstvars(fixed scalars) plusaccumulate(each name a deduped + sorted list). The source is retained so the tip re-renders whenever itsaccumulatestate grows.
Surfaces
By default a tip shows on every surface. Passsurfaces to restrict it to an allowlist:
Three shorthands cover the common groupings:
TASK_SCREENS— every per-task surface (CLI + check run + Buildkite), but not the cross-task PR summary. Use this for per-invocation tips like deep links that don’t belong in a repo-wide rollup.AGGREGATE_SCREENS— justSURFACE_GITHUB_PR_SUMMARY.SURFACE_ALL— everything (equivalent to leavingsurfacesempty).
SURFACE_CLI; omit it to keep a tip off the terminal while still rendering it on status surfaces.
Accumulating tips
For a tip that grows as a condition recurs — e.g. “these N scopes were missing across the run” — useTIP_TEMPLATE_JINJA2 with accumulate. Each emit contributes one value per field; repeated emits union them (deduped, insertion-ordered), and the Jinja2 body re-renders against the merged set:
.aspect/config.axl
combine_across_tasks = True unions the accumulated set across every sibling task into a single PR-summary row, instead of the default last-task-wins.
Customizing tips: the tip_suggestion hook
TipsTrait.tip_suggestion lets your config accept, reject, or rewrite any tip before any surface renders it. It’s the same accept/reject/replace shape as the repro_fix_suggestion hook.
.aspect/config.axl
add_tip, before the tip reaches storage.
Verdicts
A
tip_replace of title / body has no effect on a TIP_TEMPLATE_JINJA2 tip: its templates aren’t rendered until after the hook runs, so scope JINJA2 tips by id / key / type and rewrite policy fields (severity / surfaces / combine_across_tasks) instead.TipInfo fields
Examples
Veto a tip by id:.aspect/config.axl
.aspect/config.axl
.aspect/config.axl
Silencing tips
To suppress a tip without writing a hook, set theTips feature’s silence list to the ids you want to drop, or pass them on the command line:
.aspect/config.axl
id and points at this knob.
See also
- How to customize repro & fix suggestions — the sibling accept/reject/replace hook for the
🔁 Reproduce/🛠️ Fixlines. - 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.

