bus integration task
Overview
bus integration task connects bus task streams to worker execution. The
worker claims bus.task.created, bus.task.approved, bus.task.assigned, and
bus.task.reopened events for one recipient, publishes task progress, and
closes or fails the task from the selected backend result.
The default local backend is codex-appserver, which runs Codex through the
shared bus agent App Server integration. While the child Codex
session runs, the task stream is the parent control plane: bus task say
steers the active turn, approval requests are emitted as
bus.task.approval.requested, and bus task approve sends decisions back to
the pending app-server request. The container backend is still
available for provider-neutral one-shot execution through bus containers.
Completed or blocked App Server work can be reopened with
bus task reopen; the worker resumes the stored
Codex App Server thread when the task stream contains app_server_thread_id.
Local Docker App Server workers default to Codex danger-full-access mode
because the local image does not include Codex’s Linux workspace sandbox
helper; the bridge still promotes only the recipient task worktree and marks
the task blocked instead of done when the turn produces no diff or reports
incomplete evidence.
Usage
Before starting this worker, run a Bus Events API and provide BUS_API_TOKEN
with events:send, events:listen, task:read, task:send, task:reply,
and task:claim. For the default
--agent-backend codex-appserver, codex app-server and Codex
authentication/configuration must be available in the worker environment. For
--agent-backend container, also start a container integration that accepts the
requested profile and grant container:run; local testing can use
bus-integration-docker for the codex profile.
When bus task resolves a worker profile from bus-remote, the task stream
carries non-secret labels such as
worker_profile, requested model, reasoning effort, auth mode, and credential
source kind. This bridge propagates those labels into worker-start requests,
attempt evidence, lifecycle metrics, and status output while continuing to run
Codex through bus-agent/appserver. API keys, ChatGPT auth files, and
credential source refs stay in the configured worker or deployment secret
source and are not serialized through task Events.
export BUS_API_TOKEN="<token-with-task-and-container-scopes>"
bus-integration-task \
--events-url http://127.0.0.1:8081 \
--recipient bus-dev \
--agent-backend codex-appserver \
--container-profile codex
--workspace-root makes the worker run tasks from the addressed module
repository. With --workspace-root /workspace, a task addressed to @bus-dev
runs from /workspace/bus-dev, while @bus-integration-docker runs from
/workspace/bus-integration-docker.
Stack-managed workers keep claiming additional matching tasks for their
recipient after each task completes, so parallel local work does not require
manual restarts. The containers are still disposable: remove and recreate them
freely because durable coordination is in Bus Events and task file changes are
kept in the recipient-owned Git worktree and promoted commit path, not in
container-local storage. Use --once and --idle-timeout only for temporary
one-shot workers.
--workspace-recipient names the recipient that maps to --workspace-root
itself instead of a child repository. In the BusDK local task stack this is
configured as busdk, so tasks addressed to @busdk intentionally edit the
superproject root while normal module tasks keep their own recipient
repository.
--agent-backend codex-appserver keeps a running session addressable by task
reference. Use bus task say <ref> <message...> while the turn is active to
steer it directly. Use bus task reopen <ref> <message...> after a terminal
state to publish a new open task event that resumes stored App Server thread
state when present. Use bus task watch <ref> to see approval
request ids, then answer with the task ref and approval request id printed by
that watch output:
bus task approve <ref> <approval-id> accept_for_session
The approval decision must be accept, accept_for_session, decline, or
cancel.
--worktree runs each task in an isolated Git worktree. The container receives
the workspace as a read-only dependency view and receives write access only to
the task worktree. When the workspace is a Git superproject with .gitmodules,
the worker creates task-local dependency links so relative module dependencies
resolve through the read-only workspace instead of becoming writable.
After a successful task container run, the trusted bridge can stage and commit
worktree changes with --commit, then promote the task branch back to the
primary checkout with a conservative fast-forward merge. Dirty or
non-fast-forward primary checkouts fail safely. The generated checkout leaf is
unique per task, which avoids Git worktree metadata collisions for repeated or
concurrent tasks addressed to the same module.
For Codex App Server tasks, a completed LLM turn is not enough to mark work
done: no worktree changes, no tests run, an unclosed PLAN item, or explicit
blocked evidence is recorded as a blocked task stream.
When a Codex worker uses auth_mode=openai-api-key, the worker can resolve a
local deployment secret by reading /run/secrets/<ref> or
$BUS_TASK_DEPLOYMENT_SECRETS_DIR/<ref> and setting OPENAI_API_KEY
inside the worker process. The key value is never written to task Events or
repo config, and an already-set OPENAI_API_KEY takes precedence.
Workers also publish concise non-secret hourly engineering notes through
bus notes add, so the notes become Bus ecosystem data rather than hidden local
files. The logical agent_id is the recipient module/AGENTS.md identity, while
task work_ref, worker/container metadata, and App Server ids identify a
concrete run. Terminal app_server_closeout evidence includes the agent_id,
AGENTS.md path, and Bus Notes IDs or query metadata so later review can group
work by agent and inspect the notes for a specific attempt.
Lifecycle behavior is configured through a JSON policy file rather than hidden
process-global modes. --policy-file / BUS_TASK_POLICY_FILE load the
module-owned policy schema for worker count, one-worker-per-recipient behavior,
disposable versus persistent workers, idle timeout, safe App Server crash retry,
backend selection, isolated worktrees, read-only dependency worktrees,
task-scoped App Server state, evidence requirements, cleanup retention, and
progress log level. Explicit CLI flags and environment variables still override
matching policy fields for one invocation. The local BusDK dev-task Compose
stack mounts config/dev-task-lifecycle-policy.json, starts a local Notes API,
and issues worker tokens with the dev-task, events, container, and Notes scopes
needed for workers to publish bus notes evidence.
--command-json sets the command sent to the container as a JSON array. The
worker expands {prompt}, {text}, {body}, {work_ref}, {recipient},
{module}, {main_repo_path}, {repo_path}, {worktree_path}, {branch},
{worktree_branch}, {base_branch}, and {create_branch} placeholders.
{repo_path} points at the isolated worktree when --worktree is enabled.
{branch} comes from task metadata and is defaulted by bus task start to
the current Git branch when possible.
bus-integration-task \
--events-url http://127.0.0.1:8081 \
--recipient bus-dev \
--agent-backend container \
--container-profile codex \
--workspace-root /workspace \
--command-json '["codex","exec","--skip-git-repo-check","{prompt}"]'
Use --pre-command-json and --post-command-json for repository-local hooks
around the main command. Use --commit for the normal local isolated-worktree
path so task containers edit files only and the bridge handles Git metadata,
staging, and commit creation:
bus-integration-task \
--events-url http://127.0.0.1:8081 \
--recipient bus-dev \
--agent-backend container \
--container-profile codex \
--workspace-root /workspace \
--worktree \
--command-json '["codex","exec","--skip-git-repo-check","{prompt}"]' \
--commit
Remote Git push is intentionally not part of the default isolated-worktree task container path because the task container does not receive writable Git metadata for the shared workspace. The default local compose setup commits locally in the bridge and does not push.
Local Compose
The BusDK superproject includes compose.yaml for local
testing with Docker Desktop:
The default compose command for real local use runs the Codex App Server backend
in an isolated worktree for the addressed module repository, then the bridge stages and
commits successful changes before fast-forward promotion. The workspace remains
the read-only dependency view, while the addressed repository worktree is the
only writable mount for the task container. The same stack starts Bus Events,
provider-neutral container routing, PostgreSQL, and the local Notes API so
worker bus notes add calls are stored as Bus Notes data. This consumes
ChatGPT-backed Codex quota. Smoke tests can override BUS_TASK_AGENT_BACKEND
and BUS_TASK_COMMAND_JSON to use a deterministic command backend when the
goal is plumbing verification rather than real LLM work.
BUS_TASK_AGENT_BACKEND=container \
BUS_TASK_COMMAND_JSON='["codex","--version"]' \
BUS_TASK_COMMIT=false \
docker compose -f compose.yaml --profile dev-task up --build -d
docker compose -f compose.yaml --profile dev-task exec testing-agent sh
Inside the testing shell, create a task and watch the task reference printed by
the task new command:
bus task new --new-branch work/codex-version @bus-dev "Show the Codex CLI version."
bus task watch <printed-task-ref> --timeout 5m
Successful smoke output includes the container stdout from codex --version
and a completed task status. A timeout or failed container run leaves the task
with a failed status and prints the failure message in task watch.