Tasks
Definition
Section titled “Definition”A task is a durable unit of work that an agent executes and you review. It
carries a title, description, acceptance criteria, status, context, review
notes, and history. Its bundle lives under
~/, not in your repository.
Every task needs an observable outcome. Don’t file one as a scratch note.
The dashboard also shows estimated regression riskiness from task size and completed-task history. Before a diff exists, size is the declared path count; in review or done, a resolvable diff supplies the changed-line count. Insufficient history is shown as no estimate.
A task can also declare required_tools: Orbit tools its agent needs beyond
the activity’s baseline. They are fixed at creation; see
Transition rules. An operator can also grant named
read-safe tools to the implementer through granted_tools. A grant can be
changed while the task is proposed, backlog or someday when no live run,
protecting claim or preserved worktree remains; after a stopped attempt,
requeue the task and remove its preserved worktree before changing the grant
for a new attempt; see Task tool grants. The implementer
lifts that tool’s deny entry, a mutating tool stays denied, and a non-empty
grant keeps the task on its owner host.
A task can also name the tasks it waits on and link to related work. See Dependencies and relations.
Lifecycle
Section titled “Lifecycle”The common path:
proposed -> backlog -> in-progress -> review -> doneA proposed task needs your approval before a run picks it up. A task you
create directly can start in the backlog (orbit ).
Once the agent delivers, the task waits in review until you approve or reject
it.
Approval
Section titled “Approval”You pass both gates with the same command:
orbit task update "$TASK_ID" --approve --note "Scope reviewed."--approve takes the task’s next approval step from its current status:
proposed , or review . Because Orbit picks the transition,
you can’t combine --approve with field edits or --status. The note is
recorded in the task’s status history.
While a non-terminal local run lists the task in its submitted task_ids,
--approve on the CLI and Approve on the dashboard both refuse. The
error names that run, and the task, its comments, and its history stay as
they were. Stop the run, then approve. Forcing a status with --force is a
separate override and still applies. A drain authorized with
--approve-proposed approves through its own path and is not refused
because its run lists the task.
The two gates are independent:
- Entering the backlog takes an explicit approval. You give it in the
dashboard or the CLI, or your agent gives it with
orbit.when you ask; the task’s history records who. You can also authorize a local drain withtask. update orbit: while its window runs it pilots qualifyingrun auto --approve-proposed proposedtasks and approves those it verifies, including tasks filed during the window. A task qualifies with theno-diff-expectedtag, or with context files and an assessed complexity. Duplicate, already-landed, conflict or warning findings keep it proposed, as doesno-auto-approve. This is off by default and refused with--pull; only the owner approves work. An enabled CI failure sweep can also promote the repair tasks it files once its pilot validates them. - Completing out of
reviewcan instead be authorized per run with--complete, or for distributed handoffs with the owner’sworkflow.. Completion never approvesdistributed_completion = "done" proposedwork. See Completing work with--complete.
An auto-task files each recurring task at the
status its definition declares: backlog by default, or proposed when you
want to approve each one. See
Schedule Recurring Work.
Statuses
Section titled “Statuses”| Status | Meaning |
|---|---|
proposed |
Waiting for your approval. |
backlog |
Approved and queued. |
someday |
Wanted, but not actionable yet. Agents skip it. |
in-progress |
Being worked on. |
review |
Delivered and waiting for review or merge. Moving to done needs an execution_summary or a successful run. |
done |
Accepted and closed. Terminal: only a human override reopens it. |
blocked |
Paused on a dependency, a decision, or a failure. A failed run lands here only after step recovery is exhausted. If dispatch could not find the provider CLI’s launcher, the task is infra-blocked: orbit lists it under infra-blocked-tasks, and orbit returns it to backlog once the launcher resolves. |
archived |
Soft-deleted with orbit or --status . Terminal: restore it with orbit . |
rejected |
Declined. It can be reconsidered back to backlog or in-progress; any other status needs --force. |
Transition rules
Section titled “Transition rules”Every status change is checked against one lifecycle table, whether it comes
from orbit , the dashboard, or the orbit. tool:
proposed → backlog | someday | in-progress | rejectedbacklog → proposed | someday | in-progress | rejectedsomeday → backlog | in-progress | rejectedin-progress → backlog | someday | review | rejectedreview → backlog | in-progress | done | rejectedblocked → backlog | in-progressrejected → backlog | in-progress (reconsider)any open status → blocked | archiveddoneis reachable only fromreview, and it needs evidence: a non-emptyexecution_summary, or ajob_run_idwhose run succeeded. An agent can’t mark unstarted work done.doneandarchivedare terminal. A regression in delivered work gets a new task with aregression_fromrelation; the old one stays closed.- Starting work needs a plan. Moving to
in-progressfrom any status other thanbacklogis refused while the plan is empty or still the unauthored placeholder. Send--planon the same command. - Required tools are fixed at creation.
required_toolsholds exact registered tool names, normalized when the task is created. No update path (orbit, the dashboard, ortask update orbit.) accepts it afterwards.task. update
Criteria the task pilot identifies as requiring a deployed build can remain
owed after delivery. The task stays in review, even with --complete, until
an operator records every outstanding measurement as met. A successful run or
an execution summary cannot replace that evidence.
A refused transition is an invalid_input error that names both statuses and
the missing precondition.
As a human, you can override the table: orbit on the CLI, or the force (off-table) group in the
dashboard’s status menu, which asks you to confirm first. Both record a
forced event in the task’s history. The orbit. tool refuses a
force argument, so agents stay on the table.
Friction reports are separate records under orbit , not task
statuses.
Dependencies and relations
Section titled “Dependencies and relations”A task can wait on other tasks and record how it relates to them. Dependencies are the only relation that decides when a task starts. The other kinds record lineage and cross-references, and never hold a task back.
Use a dependency when one task must reach done before another can start. Use a
relation to record where follow-up or regression work came from, to link related work,
or to have a task close a friction when it finishes. Dependencies resolve inside the
workspace: a task ID that names a task in another workspace counts as missing here.
Set dependencies from the CLI
Section titled “Set dependencies from the CLI”--dependencies takes task IDs. Repeat the flag or comma-separate the IDs. task
sets them at creation, and task replaces the whole list:
orbit task add --title "Publish the API docs" --complexity low \ --dependencies "$API_TASK_ID,$SCHEMA_TASK_ID"
orbit task update "$TASK_ID" --dependencies "$API_TASK_ID,$SCHEMA_TASK_ID"orbit task show "$TASK_ID" --fields dependencies,resolved_dependenciesThe update replaces the list. To add one dependency, read the current list and send every ID you want to keep. An empty string clears the list:
orbit task update "$TASK_ID" --dependencies ""Orbit refuses a task that depends on itself and an edge that closes a dependency cycle; the error names the cycle. An ID that names no task is accepted, and the dependency is then reported as missing at admission (see Admission).
Typed relations
Section titled “Typed relations”The relations field is an array of {type, objects. It is available on
orbit. and orbit., and the CLI has no flag for it. A relation
points from the task that holds it to its target: a blocked_by relation from task A
to task B means A waits for B.
relations replaces the whole list, every kind included, so the replacement must carry
each relation you want to keep, blocked_by among them. Don’t send dependencies and
relations in the same update. Orbit applies relations last, so it overwrites the
dependency edges. To update only dependencies, use dependencies alone.
{ "id": "<task-id>", "relations": [ { "type": "blocked_by", "target": "<task-id>" }, { "type": "spawned_from", "target": "<task-id>" }, { "type": "resolves", "target": "<friction-id>" } ]}Orbit refuses a duplicate type and target pair, a relation to the task itself, and
a cycle in blocked_by or child_of. A regression object is accepted only on
regression_from.
The current kinds, each pointing from this task to its target:
blocked_by(task): this task waits for the target to reachdone. It is the only kind that gates a start. Set it with--dependenciesorrelations.child_of(task): the target is this task’s parent. It organizes work and doesn’t gate a start. Set it withaddor--parent relations.spawned_from(task): this follow-up was filed from the target. Orbit sets it on follow-up tasks it files. Set it withrelations.regression_from(task): the target introduced the defect this task fixes. Set it withadd,--source-task source_task_idon update, orrelations.supersedes(task): this task replaces the target. Orbit sets it when a sweep consolidates tasks. Set it withrelations.related_to(task): a plain cross-reference. Set it withrelations.covered_by(task ID, orgithub-pr:<number>orgithub-pr:<pull): the target handles this task’s failure. Set it withrequest URL> relations.produces(task or friction ID): this task produced the target. No shipped workflow writes it. Set it withrelations.resolves(friction ID): reachingdonecloses that friction. Set it withrelations.
regression_from takes an optional regression classification: nature is one of
broke_existing, defect_in_new, incomplete, merge_reintroduced, contract_drift
or unclassified, and escape is one of no_test, test_not_selected,
platform_only, review_missed, spec_ambiguous, nondeterministic or
unclassified. severity rates how much the defect matters on the five-level
scale review findings use — critical, high, medium, low or nit — and a
new classified link without one is refused. Every link reads back with
severity_source: filer (set by the writer, the default), verified
(finding verification confirmed or adjusted it), operator (an operator
override), backfill (a backfill of older links) or default (stored before
severity existed; reads as medium). Read a task’s relations back with
orbit .
A regression in delivered work gets a new task with a regression_from relation to the
task that introduced it. The original stays closed, as Transition rules
explains.
Admission
Section titled “Admission”A task starts only when every blocked_by target is done. An archived target counts as
done if it reached done before it was archived. A task whose targets are not yet done
waits in the backlog. orbit explains what it is waiting for.
Some targets can’t reach done by waiting: a task that does not exist in this workspace,
an archived task that never completed, or a rejected task. Run admission reports these as
task. instead of waiting. Restore the target, reopen it, or
remove its ID from --dependencies.
Only dispatch holds a task back. A manual orbit isn’t
checked against the task’s dependencies, so unmet dependencies don’t refuse it.
A task can also set an optional not_before earliest start. While it is in
the future, dispatchers hold a backlog task as scheduled without changing its
status. The hold lifts on the first admission pass after that time; approval,
dependencies and other admission checks still apply. An operator’s explicit
orbit can start named tasks early and records
the override in history. See Scheduled tasks
for setting, clearing and listing schedules.
Friction resolution
Section titled “Friction resolution”Friction records live under orbit , outside the task lifecycle. A task closes
the frictions it names when it reaches done:
- A
resolvesrelation names a friction, and reachingdonecloses it. Only relations set through the tool can name one. From the CLI, close a friction withorbit. Friction IDs have the formfriction resolve <friction-id> FYYYY-MM-NNN. - A task that reaches
donethrough a landed pull request also closes the frictions named in its description or acceptance criteria. A manual move todonereads only theresolvesrelations, not the text. - A friction that is already resolved stays resolved. Updating a task that is already
donedoesn’t resolve again. - Only frictions in this workspace close. A
resolvestarget that lives in another workspace refuses the move todone, and the error names the friction and where it lives. Remove that relation, or resolve the friction in its own workspace. - A friction ID that matches no record is not refused. Orbit records a dangling-relation
event, and the task still reaches
done.
Task tool grants
Section titled “Task tool grants”An operator can grant a task a tool that the implementing activity’s deny
list would otherwise exclude. Use a grant when the task needs a reviewed
operation, not to give an agent operator authority. required_tools is a
separate, immutable list of tools the task needs in its activity baseline;
it does not override a deny list. See Agent tool lists.
In an operator session on the owner host, inspect the task and the tool’s schema first. For example, a grant for a reviewed auto-task update can be set before dispatch:
orbit tool show orbit.auto_task.updateorbit task update "$TASK_ID" --granted-tools orbit.auto_task.updateorbit task show "$TASK_ID"orbit also accepts --granted-tools. Supply exact canonical,
registered, enabled, agent-facing names; wildcards, disabled tools and
operator-only tools are refused. An update replaces the whole grant list;
--granted-tools clears it. Only an operator may supply the field, even
to clear it. Changes are allowed while the task is proposed, backlog or
someday, with no live run, protecting claim or worktree. A preserved
candidate’s worktree keeps the grant frozen after its run stops; requeue and
remove that worktree before changing the grant for a new attempt. The write
that starts a task cannot also change its grant. Task history records changes.
Only agent_implement reads grants; reviewers, recovery and other activities
do not. A grant can lift an exact deny entry only for a tool classified
read/repeat-safe; a covering wildcard still refuses admission. All tools in
the shipped implementer’s deny list are currently classified mutating, and
unclassified tools default to mutating. A mutating grant stays denied for
live calls. Adding the same denied mutation to required_tools still
refuses implementer admission. Grants do not bypass capability, field-level,
filesystem, process or other runtime checks. A task with any grant runs on
the owner host; if its required labels or OS exclude that host, it waits
even if a follower has the right labels.
For a grantable mutation, the implementer attaches a JSON task artifact at
mutation-proposals/ using orbit.. Its content
names the granted tool, exact input and a nonempty rationale:
{"tool": "orbit.auto_task.update", "input": {"name": "<auto-task>", "description": "<updated description>"}, "rationale": "Explain why this change is needed."}Use the tool schema to choose valid input for the intended change. Orbit
refuses a proposal unless the task grants a grantable mutation and an agent
session can make that exact call. It derives an idempotency key from the
task, tool and input. The review gate presents the proposal alongside the
diff; after approval the pipeline rechecks and applies the reviewed input,
then writes a system-owned mutation-receipts/ receipt. An applied
key is not applied twice; changed input needs a new review. A failed or
unreviewed proposal stops delivery, and an interrupted pending receipt needs
operator reconciliation. A task cannot complete with an unapplied proposal.
If the agent needs the change during its run, arrange an operator action or
prerequisite task instead.
File reservations
Section titled “File reservations”Orbit locks files for you while tasks run. A task holds the files its selectors
cover while it is in-progress or review, and a task that overlaps those
files waits. These task footprint locks are automatic, and you don’t manage them.
Reserve a surface yourself when work runs outside the pipeline and needs the same protection: an editor session, a manual migration, a long refactor. A reservation is all or nothing. Orbit grants the whole surface or grants nothing, and a denial exits 3 and names who holds the overlap.
orbit task locks reserve \ --task <TASK_ID>
orbit task locks reserve \ --file dir:src/billing \ --ttl 2h
orbit task locks reserve \ --file file:README.md,dir:docs \ --ttl 45m --json--taskreserves the task’s declared context surface, expanded the way conflict admission expands it.--filetakesfile:ordir:selectors. Repeat the flag or comma-separate selectors to claim several.--ttltakes a duration such as45mor2h. It defaults to30m, and its maximum is4h.- Selectors stay inside the workspace. Symbol, module and command selectors are refused.
Reserving and releasing need the operator or runner capability, because a
reservation blocks other work from its surface until it expires or is released.
From a plain shell, prefix the command with ORBIT_OPERATOR=.
Reservations expire on their own. The TTL is the safety net if a session dies while holding one, so pick a TTL that covers the work. Once it passes, the reservation no longer blocks anything. Orbit clears expired reservations automatically.
To release a reservation before its TTL, find its ID with
orbit and pass --confirm. Release is an operator
escape hatch for a stale reservation, so use it only when the session that holds
it is gone.
orbit task locks release \ <reservation_id> --confirmTo see which files pending tasks collide on, run contention:
orbit task locks contention \ --limit 25Contention counts pending backlog tasks only, not running ones. TASKS lists
each selector that pending tasks overlap, and GROUPS counts clusters of linked
tasks. Tasks in different groups never block each other, so the group count is a
lower bound on how many tasks dispatch can admit at once, not an exact limit. A
task that declares no selectors locks nothing and is counted apart.
To see which waiting tasks these locks hold back, see Check what will actually start.
Quality bar
Section titled “Quality bar”A good task states:
- what should change
- where the change should happen
- how to observe success
- which files or selectors matter, when known
Task artifacts
Section titled “Task artifacts”A task artifact is a file stored on the task, outside git, so you or an agent
can open that evidence later. Use one for a note, a log, a check result, or a
screenshot. The bytes live in the task bundle under
~/.
There is no artifact-store setting. If a config file still sets
[task] , Orbit refuses to load it until you remove the key.
Current task artifacts are always enabled.
Attach, list, and retrieve
Section titled “Attach, list, and retrieve”Write the file under . ($ORBIT_SCRATCH_DIR). put reads that
source file and stores a copy under a logical path on the task. Listing names
comes from orbit , not from a separate artifact command. get
prints text.
mkdir -p .orbit/tmpprintf 'checked the build\n' > .orbit/tmp/notes.txtorbit task artifact put "$TASK_ID" .orbit/tmp/notes.txt --path evidence/notes.txtorbit task show "$TASK_ID" --fields artifactsorbit task artifact get "$TASK_ID" evidence/notes.txtThe source path and the logical path are not the same thing.
- Source path.
<SOURCE_PATH>is a file on disk. Orbit resolves it against the current directory, not the checkout root, then confines it to the workspace after resolving symlinks. A file outside the checkout, including anything under/, is refused. A file that exists only at the checkout root is refused with the absolute path to pass.tmp - Logical path.
--pathis the name stored on the task, relative to that task’s artifacts directory. It defaults to the source file’s name. It is not a filesystem path: an absolute path or.is rejected.. gettakes this stored name, the same stringorbitlists. The field is metadata only: path, media type, size, and who attached the file when that was recorded. It does not include the bytes. There is no list subcommand undertask show <ID> --fields artifacts orbit, onlytask artifact putandget.
The media type comes from the file name. An unknown extension is stored as
application/.
Attaching a file, and reading it back with get or
orbit., both refuse more than 1 MiB (1,048,576 bytes). A
file already stored on the task can still be downloaded from the dashboard
when it is larger than that.
A logical path that begins with review-, in any letter case, is reserved
for the review gate. The only exception is the schema-checked
review-report.. Name ordinary evidence something else, such as
examination-checks..
put and get follow the task to the host its ID prefix names when you pass
no --workspace. Pass --workspace to read or write a local mirror instead.
See Tasks.
For a short text payload with no file, on this machine:
orbit task update "$TASK_ID" --artifact evidence/notes.txt=checkedRepeat --artifact for more than one. That flag does not follow the task to
another host. Write the bytes to a file and use orbit ,
which does.
Binary files
Section titled “Binary files”get prints text. An image or any other payload that is not printable text
is not printed; the error tells you to save it with --out.
orbit task artifact put "$TASK_ID" .orbit/tmp/diagram.png --path screenshots/diagram.pngorbit task artifact get "$TASK_ID" screenshots/diagram.png --out .orbit/tmp/diagram-out.png--out writes the stored bytes to that file. It exists only on the CLI.
From an agent
Section titled “From an agent”The MCP tools are orbit. and orbit..
Both run in the connected workspace.
orbit.takestask. artifact. put idandsource_path, and an optionalpathfor the logical name. The same confinement, size, and reserved-name rules apply. Passmodelwhen you want that agent family recorded on the write.orbit.takestask. artifact. get idandpath(the logical path). List names first withorbit.andtask. show fieldset toartifacts. An optionalworkspaceargument is a fail-closed filter: a workspace that does not own the task is not found. There is nooutargument.
Text comes back as UTF-8 in content (encoding is utf8). Everything else
comes back as base64 in content_base64 (encoding is base64). Every read
includes media_type, size, and presentation: text, image, or
opaque.
PNG, JPEG, GIF, and WebP are image only when the bytes match that type. A
client that can show images then receives the picture as well as the base64.
SVG, HTML, and any payload whose bytes do not match its declared type are
opaque. They are never rendered. You can still save them with --out, or
download them from the dashboard.
Dashboard
Section titled “Dashboard”Open the task and choose Artifacts. With nothing attached, the tab says “No artifacts attached.” Each row shows the logical path, the media type, and the size. Expand a row to preview it.
PNG, JPEG, GIF, and WebP are shown as images, with Open and Download. If the browser cannot decode one, the tab says the image could not be decoded and still offers the download. Plain text, CSV, JSON, and YAML are shown as text. HTML, SVG, Markdown, and other files are downloads, not rendered pages. The tab does not attach files. See Use the Dashboard.
Review layers
Section titled “Review layers”Orbit can review a delivery at three points. Before-PR and before-landing
review are alternatives: review. selects at most one of them.
After-landing review is a separate switch and can be enabled alongside either
one. review. defaults to off; the after-landing auto-task ships
disabled.
| Layer | When it runs | What it can change | How to enable it | What it blocks |
|---|---|---|---|---|
| Before-PR | Before Orbit opens the pull request. | The reviewer can fix the candidate before the PR exists. | Set review. with orbit . |
PR creation waits for review and fixes. It requires the PR delivery route. |
| Before-landing | After Orbit opens the pull request, while hosted CI runs. | The reviewer can fix the PR head; Orbit validates and pushes the fix onto the open PR. | Set review. with orbit . |
Merge waits for an approved, settled head. Any other outcome leaves the PR open and the task in review. |
| After-landing | In batches after deliveries land. | The review task records confirmed findings as follow-up bug tasks; it does not change an already-landed delivery. | Run orbit . |
It does not block a delivery from landing. A batch remains owed until it has valid review evidence. |
On a fresh workspace, the first orbit needs
--seed-from-global to create its config with your current security settings,
for example orbit .
The dashboard’s Settings page shows all three switches and their source. For the
same effective state, use orbit or orbit ; the first two
reviews show their time limit, and after-landing shows when its next batch is
due. After-landing review is independent of the choice between before-PR and
before-landing review.
For bounded low-risk work, a workspace can map a task tag to skip all three
agent review layers when every delivered task carries it, every changed path
is allowed, and its optional guard passes. See
Review-skip tags for configuration,
tag authority and the guard contract. These profiles preserve required
validation and hosted CI; the built-in skip-review and skip-validation
tags have separate rules.
Adding review to one run
Section titled “Adding review to one run”orbit and orbit accept --review or
--review . The dashboard’s Start a window card has the same
choice under Review before landing. The option adds a review layer to that
run only and leaves workspace configuration unchanged. It can only add review,
never remove or swap it:
| Workspace policy | --review |
--review |
|---|---|---|
| Applies no layer | Adds before-PR review | Adds before-landing review |
review. |
Accepted; policy already applies it | Refused, naming the key and its source |
review. |
Refused, naming the key and its source | Accepted; policy already applies it |
Before any run is created, Orbit also refuses the option when:
- neither
workflow.norpurpose_crews. pre_landing_review workflow.is set, because there is no reviewer crew to run it;purpose_crews. review - the run uses local-only delivery, which has no pull request to review; or
- the run is a pull drain (
orbit), because the owner decides review for the claims it hands out.run auto --pull
In a drain, a task routed to local-only delivery waits in the backlog for the
window and is not started. Child runs inherit the layer from their parent, and
a resumed run keeps the layer it captured. orbit reports the layer
and its origin on its Review: line, and the dashboard’s run detail page shows
it in the review cell. The option never adds after-landing review, which
stays the delivery-code-review auto-task.
Write testable acceptance criteria. Prefer “command X exits successfully” or “file Y contains Z” over “the behavior feels better.”