Skip to content

Tasks and Checks ​

When the agent delegates, each piece of delegated work is a task: a worker with a role — planner, researcher, architect, coder, reviewer, tester, executor or security_guard — runs it and hands the result back. This page covers how tasks run, what comes back from them, whose they are, and how to have a coding task's work checked by your own tests before it counts as done.

Workers show in the Sub-Agents panel (Ctrl+F, or /subagents), and /btw gives a read-only summary of them that never enters the agent's context. The /task commands act on the session's task list, the plan the agent keeps for its work; an item there can stand for a delegated task.

Document Author: Uni Zhu

How tasks run ​

A task starts only once every task it depends on has finished the way it needs to. Everything in UGENT that starts tasks applies that same rule, and a task whose turn is still running is never started a second time.

Every ending reaches the work that depends on it, once:

  • Tasks that need a task to succeed fail with it — the whole chain that needs it, not just the next link.
  • A parent waiting on a task is resumed when the task ends, however it ended — completed, failed, cancelled, timed out, or dropped from the plan — and is told how.

A completion is judged once. When checks decide it (see Checking coding tasks), the task stays running until the verdict lands, and the time the checks take does not count against the worker's time limit.

After a restart ​

A restart repeats nothing that may have acted. The previous run's tasks are not run again: one that had started is marked interrupted, because it may already have written files, sent messages or called services, and one still queued is marked not run. A parent that was waiting on them is resumed, told how each ended, and told to ask you before redoing the work rather than redoing it on its own.

What comes back ​

A worker ends its report with four parts — RESULT, SUMMARY, EVIDENCE and NEXT — and the parent receives a handoff for every task it waited on, failed and missing ones included, in the order it asked:

  • the outcome: completed, partial, blocked, failed, cancelled or no result;
  • a summary;
  • the evidence: each file, command and test, with its outcome;
  • what is left unresolved;
  • what checked it, when checks did: Checks: accepted for snapshot <revision>; receipts …, or the decision and its reasons.

The task's state decides failed, cancelled and no result. For a completed task its report decides, by its last RESULT: line; an example quoted or indented in the report never counts. So a task whose worker finished but whose report says failed is handed on as failed.

Handoffs share a size budget. The outcome, a failure's reason (up to 400 characters) and a handle to the full report always survive; evidence and unresolved items are capped in number and length, with the rest counted, and the summary is what is cut to fit. A fan-out too large for that gives its finished tasks a line each. The agent can read any task's whole report when it needs the rest (delegate_status, 12,000 characters a page). A worker that depends on other tasks starts with their handoffs.

Whose task it is ​

A task acts for the user whose turn created it. The runtime records that user on every task — from a delegation, a follow-up, a plan revision, a quality-gate escalation, or a routing action that delegates — and nothing in a tool's arguments can name it.

  • The worker runs as that user: their memory, the files they may reach, and what the context engine serves them. Whatever it delegates in turn acts for the same user.
  • A parent resumed after its workers finish runs as the user it was suspended as, after a restart too.
  • A task with no user acts for nobody: no memory, no personal files, and a context engine that requires a user refuses it. It never falls back to the owner; only turns typed at the TUI or REPL, and cron and autorun jobs, do.
  • One user's tasks do not exist for another user's turns: checking on them, following up, and revising the plan all treat them as unknown.
  • A task sent to a swarm peer carries its user too. The peer runs it as that user, or for nobody, never as its own owner.

Which models workers use ​

When any LLM instance sets allow_subagent_use = true, workers may use only the instances that opt in (see Sub-Agent Cost Control). That holds at every dispatch, not only when the agent names a target:

  • A fallback chain offers a worker only approved instances, and a worker's failover mid-turn skips unapproved ones and the parent's default.
  • A worker created without a target, such as a guardrail scan or a task a plan revision adds, runs on the first approved instance rather than the parent's default.
  • A dispatch to an unapproved instance fails with Provider target '<name>' is not approved for sub-agent use, and the parent's handoff says so.

With no instance opted in, every configured instance stays eligible.

Checking coding tasks ​

Off by default. A worker saying it is done is a claim. With [task_acceptance] configured, a delegated task of a role you name is complete only when your own test command agrees:

toml
[task_acceptance]
enabled = true

[[task_acceptance.profiles]]
name = "unit-tests"
roles = ["coder"]
command = "cargo test --workspace --quiet"
report = "libtest"
min_tests = 1
timeout_secs = 900
attempts = 3

When the worker of such a task reports completion, the command runs in a fresh copy of the workspace as the worker left it. The task completes only when the command exits zero and its report shows at least min_tests tests ran and none failed. Otherwise the task goes back to its worker with the reasons. attempts counts the completions judged, so the worker is sent back attempts - 1 times before the task fails. No tests run, fewer than min_tests, a failing test, and a report that was never written each keep their own reason, and none is ever read as a pass.

Checks take turns: one runs at a time, each against its own copy, so a worker editing the workspace meanwhile changes nothing a check sees. A workspace that changed after its worker finished and before its check could start is reported as not checkable, not blamed on the worker.

KeyDefaultMeaning
enabledfalseWhether tasks are checked at all. While on, the section must hold exactly one [[task_acceptance.profiles]]
namenone, requiredThe profile's name and its check's: 1 to 32 characters of a-z, 0-9, - and _, not starting or ending with -
roles["coder"]Roles of the tasks it covers
commandnone, requiredThe test command
reportnone, requiredHow the result is read: libtest, vitest-json or pytest-junit
min_tests1The fewest tests that must run, 1 to 1,000,000
timeout_secs900How long the command may run, 1 to 3,600
attempts3Completions judged before the task fails, 1 to 10

Reading the test report ​

reportCount read fromExample command
libtestThe summary cargo test printscargo test --workspace --quiet
vitest-jsonA Vitest JSON report written to $UGENT_TEST_REPORTnpx vitest run --reporter=json --outputFile="$UGENT_TEST_REPORT"
pytest-junitA JUnit XML report written to $UGENT_TEST_REPORTpytest --junitxml="$UGENT_TEST_REPORT"

UGENT_TEST_REPORT names a file in a directory made for that attempt alone, so a report left in the workspace, or by an earlier attempt, counts for nothing. When the section is read, a file-report profile whose command never mentions the variable is warned about: unless the command reads it some other way, every task will be rejected for want of a report.

Which tasks are covered ​

A task is covered when it was delegated under a role the profile names, acts for a user, and names the conversation it was delegated from. A task of a covered role is left unchecked, and the log says why, when it acts for nobody, names no conversation, or is placed on a swarm peer, since its checks read this host's workspace. One that cannot be bound for another reason — its restrictions forbid running commands, for instance — also runs unchecked, and nothing then shows it as verified.

The command, the minimum, the timeout and the attempts come from your configuration, and the owner and the conversation from the runtime's own record of the turn. Nothing a model writes reaches them: the delegation tools ignore any plan or gate a model supplies.

A quality gate, not enforcement

Coverage is the delegating model's choice: a coding job delegated under another role gets no check. And the worker writes the tests the command runs: min_tests bounds how few tests count, not whether the worker weakened them. Treat this as a check on honest delegation, not as a guarantee.

Before you turn it on ​

  • The workspace must be readable whole. The checks run in the runtime's workspace root, digested as the goal gate does it: in a git repository, what git can see plus ignored .env files; elsewhere, every file but build and cache directories. One file the service user cannot read leaves every candidate unchecked, and the worker is told its result could not be checked.
  • The copy holds inputs only. Nothing git ignores is copied apart from .env files, so there is no target/, node_modules/ or virtualenv in it, and builds start cold. The command must work from a clean checkout, with the test runner installed for the service user. Allow for that in timeout_secs.
  • Links and special files. An input reached through a link, a link by absolute path or one leading out of the workspace, a checked-out submodule, and a FIFO or socket cannot be copied exactly, so a candidate holding one is left unchecked.
  • Temp space. Each copy is made under the system temp directory (TMPDIR), which must have room for the workspace and allow programs to run.
  • The sandbox. The command runs as a login shell of the service user, under the OS sandbox's tooling profile when the sandbox is in force. It runs the project's tests, which are code a model wrote, whatever --disable-bash says.
  • Where it applies. The daemon and the REPL read this section; the TUI does not.

Confirm the switch took by the log line task acceptance is on.

Mistakes in the section ​

An unknown key or an out-of-range value inside [task_acceptance] stops the configuration loading, so a host never starts checking less than it was told to. A misspelled section name is not noticed: the file loads as if the section were absent, and the log says task acceptance is off. A role that is not one of the built-in roles is warned about once, when the section is read.

What you see ​

  • The parent agent gets a Checks: line in the task's handoff, naming the decision, the revision it was taken for, and the receipts it rests on.
  • The web workspace shows, under the conversation's turn status, a line for each checked task; see What the labels mean.
  • The log says what happened to each task:
Log lineSays
a task's acceptance plan is boundThe task's checks are set up
a task of a covered role is not checkedA covered task was left out, with the reason
a task's checks judged its completionThe verdict on one completion
a task's completion could not be checkedA warning: the checks could not run

Turning it off ​

Set enabled = false. The daemon and the REPL pick up the change without a restart. A reload, like a restart, stops the running runtime first: a check in progress is stopped, nothing is accepted for it, and a conversation waiting on its task is told the task was interrupted. Change the file between checks if none should be cut short.

Stopping a turn ​

Stopping a turn, with the web chat's Stop for instance, stops the tasks it delegated and any check running for them. The check's processes and its copy are gone within seconds, and the decision is recorded as cancelled, never as accepted.

The conversation can go on afterwards. A tool call the stop kept from running is answered Tool execution cancelled: <name>. One cut off another way, mid-call or by a host that died, is answered Tool execution interrupted before it returned a result: <name> in every later request. Provider APIs that refuse a history holding an unanswered tool call therefore accept the next turn.

Verified means checked ​

A finished turn, a worker saying it is done, and a result being verified are three separate facts. For each checked goal or task, UGENT keeps a record of the third: what was judged (the exact version of the workspace, by digest), which checks ran against it, how each ended, and the decision taken from them.

  • Only an accepted decision counts as verified, and only when every required check passed for exactly that version.
  • A check that failed for a version stays failed for it: a flaky check cannot pass on a retry of the same files.
  • A decision is about one version. A later change is a new candidate, checked again.
  • Each piece of work belongs to its owner, and a person reads only their own decisions. A check's output stays on the host.
  • Checks a stopped process left open are closed as interrupted after the restart; nothing is rerun.

Goals use the same record when a verification gate is configured.

What the labels mean ​

The web workspace labels each checked goal or task of a conversation from that record alone, so the line is the same after a reload, in a second tab, and after a restart.

LabelMeans
Verified snapshot <revision> (<checks>)Every required check passed for this version of the workspace. The checks are listed: tests for a task checked by [task_acceptance]; command-0, command-1 and so on, in order, for a goal's gate commands
Checks pendingA required check has not reported yet
Checks failedThe checks, or the worker's own report, rejected it
Verification blockedNo verdict could be reached: a check timed out, could not run, or was refused
Checks out of dateThe evidence is for another version or plan than the one judged
Checks cancelledThe work or its checks were stopped
Evidence unavailableA record the decision rests on is no longer kept, so the page will not vouch for it

Two more labels are for checks that judge an output rather than a workspace: Citations pinned <revision> for a research answer whose every citation names a pinned source — which says where each claim comes from, not that the source says it — and Preservation checked <revision> for a translation that kept its source's numbers, links, placeholders and markup. No ugent.toml setting produces those checks; [task_acceptance] runs tests against a workspace.

A finished turn still reads only as finished: completion and verification are shown apart.

Released under the Private Beta License.