ctx.hooks lets your .aspect/config.axl or a feature run AXL at the two seams of that lifecycle:
ctx.hooks.pre_task(fn)runsfn(ctx)right before the task body.ctx.hooks.post_task(fn)runsfn(ctx, conclusion)right after it, however the body ended: a normal return, an earlyctx.std.process.exit, or an error.
ctx a hook receives is the running task’s TaskContext, the same one the body gets, so a hook can read ctx.task, ctx.args, and ctx.traits, call ctx.std, or make HTTP requests. conclusion is the TaskConclusion the runtime resolved for the task.
Task hooks were added in Aspect CLI v2026.38.16. Pin that version or newer in
.aspect/version.axl; see version pinning.Register hooks from config.axl
Hooks registered in config.axl apply to every task in the repository. This is the place for repository-wide policy: a precondition every task must meet, or a check on how every task ran. The example below refuses to run on a dirty working tree, and warns when a task that passed took longer than its budget, a signal that the remote cache or the runner size needs a look.
.aspect/config.axl
config closes over: _stamp_start records when each task began, and _check_budget reads it back once the task has ended. Only a passing task is measured, since a failure has a more important story to tell.
An exit inside a pre-task hook stands in for the body. If _require_clean_tree refuses, the task never runs, the message prints as an ERROR: line with no traceback, and the post-task hooks still run with that conclusion.
Register hooks from a feature
A feature can register the same hooks from its implementation. Package the behavior as a feature when it should be reusable across repositories, or when it needs the feature’s ownargs.
.aspect/verdicts.axl
_notify with outcome.exit_code == 1 and the failure text as outcome.message; the traceback still prints afterwards, as it would without the hook.
Register a post-task hook from the task body
A task body can register post-task hooks too. Use one instead ofctx.defer when the cleanup depends on how the task ended, such as keeping a scratch directory for inspection only when the task failed.
.aspect/lint.axl
config.axl or a feature.
What a post-task hook receives
Order of execution
For a task with hooks from every source, the runtime runs:- Pre-task hooks, in registration order. Hooks from
config.axlcome before hooks from features, since configs evaluate first. - The task body.
- Post-task hooks, in registration order.
ctx.defercallbacks, in reverse registration order.- The closing bookend.
WARNING: line and does not change the task’s exit code; the remaining hooks still run. The same is true of a failing ctx.defer callback.
Hooks or defer?
The built-in
build and test tasks use a post-task hook themselves: when a task ends before its normal conclusion, the hook closes the GitHub check run, Buildkite annotation, or GitLab commit status with the real verdict and the message the task ended on.
Related
- Cleanup with defer in the basic guide.
- The
TaskContext,FeatureContext, andConfigContextreferences list every attribute available to a hook: TaskContext, FeatureContext, ConfigContext.

