Task Execution Profiles
Typed task-owned overlays for coordinator guidance, worker selection, reviewer routing, participant policy, and sandbox mode at session start.
- Audience
- Operators running durable agent work
- Focus
- Autonomy guidance shaped for scanability, day-two clarity, and operator context.
A task execution profile is a typed, task-owned overlay that shapes how the runtime executes one
task without giving the profile any new authority. Profiles steer coordinator guidance, narrow which
workers and reviewers are eligible, bound participant routing, and pick the session sandbox. Task
ownership, lease authority, terminal state, and review verdicts stay in task.Service exactly as
described in Task Runs and Leases and
Coordinator Handoff.
What a profile contains
Every task has at most one execution profile. The shape is fixed and persists through typed columns
and selector side tables (no metadata_json):
{
"task_id": "task-123",
"coordinator": {
"mode": "inherit",
"agent_name": "",
"provider": "",
"model": "",
"guidance": ""
},
"worker": {
"mode": "inherit",
"agent_name": "",
"provider": "",
"model": "",
"allowed_agent_names": [],
"preferred_agent_names": [],
"required_capabilities": [],
"preferred_capabilities": []
},
"review": {
"agent_name": "",
"provider": "",
"model": "",
"allowed_agent_names": [],
"preferred_agent_names": [],
"allowed_channel_ids": [],
"preferred_channel_ids": [],
"allowed_peer_ids": [],
"preferred_peer_ids": [],
"required_capabilities": [],
"preferred_capabilities": []
},
"participants": {
"allowed_channel_ids": [],
"preferred_channel_ids": [],
"allowed_peer_ids": [],
"preferred_peer_ids": [],
"allowed_agent_names": [],
"preferred_agent_names": [],
"required_capabilities": [],
"preferred_capabilities": []
},
"sandbox": {
"mode": "inherit",
"sandbox_ref": ""
}
}| Block | Purpose |
|---|---|
coordinator | inherit keeps the workspace coordinator behavior. guided keeps the same daemon-managed coordinator but injects task-specific agent_name/provider/model/guidance. |
worker | inherit keeps workspace defaults. select narrows worker eligibility through allowed_agent_names, preferred_agent_names, and capability selectors. |
review | Reviewer hints (agent/provider/model) plus reviewer routing selectors (channels, peers, agents, capabilities) consumed by the review gate. |
participants | Upper-bound routing policy for coordination surfaces and review routing. Allowed lists narrow; preferred lists rank. Never a permission grant. |
sandbox | inherit falls through to workspace defaults. none skips sandbox startup when the runtime allows it. ref resolves a named [sandboxes.<name>] profile at session start. |
Persistence and validation live in internal/task/profile.go and the typed selector tables in
internal/store/globaldb; they ship as a numbered migration, not a JSON column.
Selector precedence and runtime selection
Effective behavior at runtime is the result of layering, not negotiation. The order is:
- Config defaults.
[task.orchestration.profile]and[task.orchestration.review]define the default coordinator mode, default worker mode, default sandbox mode, provider-override gate, sandbox-nonegate, and review policy defaults. See the config-toml reference. - Persisted profile (write time). When an operator or agent calls
task.Service.SetExecutionProfile, the service trims, deduplicates, and normalizes selectors, defaults missing modes toinherit, and validates the result against the config gates. The normalized profile is stored intask_execution_profilesplus the typed selector tables. - Claim eligibility.
ClaimNextRunconsults the worker side of the persisted profile to filter eligible sessions: exactworker.agent_name,worker.allowed_agent_names, and required capabilities are enforced as store-level claim filters before a session is allowed to claim the run. - Session start (load only). When task service starts a worker session it loads the persisted
normalized profile (or the default
inheritprofile when none is stored) and passes it intoStartTaskSession. The daemon session bridge then mapsworker.agent_name,worker.provider,worker.model, and the sandbox policy intosession.CreateOpts. Session start does not re-run profile validation; provider and model still flow throughconfig.ResolveSessionAgentWithRuntime, so profile values never bypass provider authorization, credential slots, or session validation. - Coordinator routing. The daemon
ReviewRouterand coordinator runtime read review and participant policies to pick reviewer sessions. The original worker is excluded; persistedtask.Serviceis the only authority that records routing diagnostics or no-route verdicts. - Continuation runs. When a review rejects a run, the continuation run uses the task's current profile at enqueue time. Reviewed-run worker/participant fields are copied only when the new profile leaves the equivalent selector empty.
task.Service rejects profile mutation while tasks.current_run_id is set. Profile edits are
control-plane changes; they must not rewrite an active execution.
Sandbox mode behavior
| Mode | Effect at session start |
|---|---|
inherit | The session keeps the workspace/global sandbox path. No task-level override is applied. |
none | The session starts with no sandbox. The gate is enforced at profile write time when task.orchestration.profile.allow_task_sandbox_none = false. Tool policy, approval policy, provider authorization, and session authorization still run. |
ref | sandbox.sandbox_ref resolves through workspace [sandboxes.<name>] validation. Unknown or invalid refs fail with a typed validation error before session start. |
Sandbox selection never bypasses tool allowlists, approval policy, or provider authorization. It only chooses which sandbox profile (if any) the session boots with.
Worker runtime selection
The worker block resolves at session start as follows:
agent_name(when set and allowed) becomes the session agent.providerandmodeloverlay the agent definition. Thetask.orchestration.profile.allow_task_provider_overridegate is enforced at profile write time; if the gate isfalse,task.Servicerejects any profile that sets a non-empty provider or model on the coordinator, worker, or review block. Persisted overrides flow throughconfig.ResolveSessionAgentWithRuntimeat session start so provider authorization, credential slots, and session validation apply unchanged.allowed_agent_namesand capability selectors filterClaimNextRuncandidates so an agent/session that does not match cannot claim the run.
If the resulting selector matches no eligible session, the run stays queued. The runtime exposes the state through dashboard, scheduler health, and active-run diagnostics rather than auto-relaxing the profile.
Manage a profile from the CLI
The agh task profile command group operates against a single task id and shares its UDS surface
with the matching HTTP endpoints. Every subcommand supports -o json|jsonl|toon for agent
consumption.
agh task profile inspect task-123 -o json
agh task profile update task-123 --profile "$(cat profile.json)" -o json
agh task profile delete task-123 -o jsonGenerated reference for each verb:
Update is a full replace, not a patch. The request body must carry the entire profile; any section
that is omitted, sent as {}, or sent with an empty selector list normalizes to its default
(missing modes become inherit, missing selector arrays become empty). To preserve a previously
persisted selector, re-send it in the new body. Service-level normalization trims fields,
deduplicates selectors, sorts them deterministically, and rejects values outside the
[task.orchestration] gates with a typed validation error.
Manage a profile through HTTP and UDS
Both transports mount the same shared core handlers, so HTTP and UDS responses are identical and refer to the same authority.
| Method | Path | Purpose |
|---|---|---|
GET | /api/tasks/{id}/execution-profile | Read the persisted profile or the default inherit profile if none is stored. |
PUT | /api/tasks/{id}/execution-profile | Replace the profile. Body must satisfy task_execution_profile.task_id == {id} (or omit the field). Omitted blocks normalize to defaults; PUT does not patch fields. |
DELETE | /api/tasks/{id}/execution-profile | Remove the profile. The task falls back to the default inherit profile. |
Operation IDs in openapi/agh.json: getTaskExecutionProfile, setTaskExecutionProfile, and
deleteTaskExecutionProfile. The OpenAPI document is the source of truth for request/response
shapes; do not paraphrase fields. Generated TypeScript types live under
web/src/generated/agh-openapi.d.ts.
PUT and DELETE return 409 Conflict when the task has an active run
(tasks.current_run_id is set). Resolve or release the active run before retrying.
Native tools for in-session agents
Worker, coordinator, and reviewer sessions can manage profiles through the task toolset. The
model-facing names live in the tasks toolset; AGH registers them internally with the agh__
prefix so toolset routing can hide them when policy denies the call.
| Model-facing name | Internal id | Authority |
|---|---|---|
task_execution_profile_get | agh__task_execution_profile_get | Reads the profile through task.Service. Read-risk, hidden when off. |
task_execution_profile_set | agh__task_execution_profile_set | Updates the profile through task.Service. Mutating, active-run guard. |
task_execution_profile_delete | agh__task_execution_profile_delete | Deletes the profile through task.Service. Destructive, active-run guard. |
Native tools delegate every read and mutation to the task service. They do not bypass active-run mutation rejection, normalization, audit events, default-profile behavior, or claim-token fencing. Reviewer-bound submission of a verdict still uses the dedicated reviewer tool described in the review-gate documentation; the profile tools never assert reviewer authority.
Inspect and edit from the operator web UI
Open a task on the web UI and switch to the Orchestration tab. The tab is rendered by
TasksDetailOrchestrationPanel and is wired against the task 26 data layer in
web/src/systems/tasks.
The tab surfaces:
- Execution Profile. Shows the effective profile (or the default state when none is persisted).
The JSON edit dialog calls the same
PUT /api/tasks/{id}/execution-profileendpoint as the CLI and the nativetask_execution_profile_settool. Edit and Delete buttons are disabled while the task has an active run; the UI mirrors the runtime's mutation guard rather than inventing a parallel rule. - Reviews. Read-only list of persisted task-run reviews (status, outcome, missing work, next
round guidance). Verdict authority remains the reviewer-bound
submit_run_reviewtool; the card surfaces a permanent disclaimer reinforcing that operator sessions cannot submit a verdict from the web UI. - Bridge Notifications. Cursor diagnostics for terminal task notifications, including a zero state when no delivery has happened.
- Stream Resume.
latest_event_seqand connection state for cursor-seeded SSE.
Server rejections (validation errors, active-run guard, missing task) surface as toasts. The web UI
does not infer authority locally; it reflects what task.Service returns.
Config lifecycle
Defaults and gates live under [task.orchestration]. The complete reference is in
config.toml; the relevant subset for profiles is:
[task.orchestration.profile]
default_coordinator_mode = "inherit" # inherit | guided
default_worker_mode = "inherit" # inherit only
default_sandbox_mode = "inherit" # inherit | none
allow_task_provider_override = true # gates worker.provider/model and review.provider/model
allow_task_sandbox_none = true # required when default_sandbox_mode = "none" or task.sandbox.mode = "none"Behavior to keep in mind:
- Unknown TOML keys are errors. The config loader rejects typos at startup; profiles inherit the same strictness.
<workspace>/.agh/config.tomloverlays the global file. Workspace overlays can flipallow_task_provider_overrideorallow_task_sandbox_nonefor one workspace.task.orchestration.profile.*paths are agent-mutable through the existing config tool surface (agh config setand theagh__config_*native tools). Secret-shaped paths and trust-rooted sections remain off-limits; see the config tool surface invariants in config.toml.- Sandbox
noneis rejected at validation time whenallow_task_sandbox_none = false, even if a task explicitly setssandbox.mode = "none". - Provider/model overrides set on the profile are rejected at validation time when
allow_task_provider_override = false. The same gate applies tocoordinator,worker, andreviewblocks.
The review-policy defaults under [task.orchestration.review] are documented with the review gate;
they shape task.review_policy and the reviewer routing applied through ReviewProfile.
Authority boundary
Profiles are configuration and selection input. They never become authority on their own:
task_runsremains the only durable execution queue and ownership source.ClaimNextRunis the only authoritative claim primitive. Profile selectors narrow it; they cannot grant a claim that lease/scope rules would deny.task.Serviceowns task/run transitions, profile validation, terminal state, review verdicts, and continuation-run creation.ParticipantPolicyconstrains routing only. Channel membership, peer authorization, and tool policy are still enforced by the network, bridge, tool, and review subsystems.SandboxPolicyselects a sandbox; it never bypasses approval policy, tool allowlists, provider authorization, or session authorization.CoordinatorProfile.mode = "guided"keeps the daemon-managed workspace coordinator and respectsmax_active_per_workspace. There is no dedicated per-task coordinator in this MVP.
Related pages
- Task Runs and Leases explains the claim/lease contract that profile selectors narrow.
- Coordinator Handoff explains coordinator bootstrap and how
guidedmode injects task-specific guidance. - Coordination Channels explains why channels carry conversation but never own task state.
- config.toml lists every
[task.orchestration]field, default, and validation rule. - agh task profile is the generated CLI reference for the profile commands.