pub struct Task {Show 15 fields
pub schema: u32,
pub id: String,
pub title: String,
pub instruction: String,
pub repo: PathBuf,
pub source: Source,
pub priority: i32,
pub solo: bool,
pub status: TaskStatus,
pub attempts: usize,
pub runs: Vec<String>,
pub last_error: Option<String>,
pub hold_reason: Option<String>,
pub created_at: Timestamp,
pub updated_at: Timestamp,
}Expand description
One unit of work.
Fields§
§schema: u32On-disk format version.
id: StringTask id, e.g. 20260902-140501-a1b2.
title: StringOne line, for lists and notifications.
instruction: StringThe task itself, handed to the graph verbatim.
repo: PathBufRepository to work in.
source: SourceWho asked.
priority: i32Higher runs first; ties break oldest-first so nothing starves.
solo: boolRun this task alone: one implementer, no panel of judges to convince.
#[serde(default)] so a queue file written before this field existed
still reads, as false - the ordinary multi-candidate competition,
unchanged. A task set to solo still runs the whole graph; only the
candidate count the daemon builds it with changes, and
crate::graph::Runner already collapses a single-candidate run to
implement → review → gate → merge on its own (see
crate::graph::Runner::review’s doc), so nothing about judging,
deliberation or voting had to change to support this.
status: TaskStatusCurrent state.
attempts: usizeHow many times this task has been claimed.
runs: Vec<String>Runs this task has produced, oldest first.
last_error: Option<String>Why the last attempt did not land.
hold_reason: Option<String>What a human hold is waiting on.
None covers both the ordinary cases: a hold the loop makes itself
(out of attempts, or the disk gate closed) explains itself through
Task::last_error instead, and a human hold nobody bothered to
explain is still a valid hold. The queue has no way to express a
dependency between two tasks, so on the occasions a hold really is
“wait for that other task first”, this is the only place that reason
survives - see Task::hold and Task::release.
#[serde(default)] so a queue file written before this field existed
still reads, with no reason recorded rather than a parse error.
created_at: TimestampWhen the task was filed.
updated_at: TimestampLast change to this file.
Implementations§
Source§impl Task
impl Task
Sourcepub fn new(
title: String,
instruction: String,
repo: PathBuf,
source: Source,
) -> Self
pub fn new( title: String, instruction: String, repo: PathBuf, source: Source, ) -> Self
File a new task. Persist it with Queue::put.
Sourcepub fn succeed(&mut self)
pub fn succeed(&mut self)
Record a successful run.
Both magi task done and POST /api/queue/{id}/done can close a held
task directly, with no release in between, so this clears
hold_reason the same way Task::release does. Otherwise a task
held for “waiting on 3ed9” and then closed as done without ever being
released would still read as waiting on something in magi task show
and on its card, after it no longer is.
Sourcepub fn fail(&mut self, why: impl Into<String>, max_attempts: usize)
pub fn fail(&mut self, why: impl Into<String>, max_attempts: usize)
Record a failed attempt. Out of attempts means held for a human, rather than retried until the money runs out.
Sourcepub fn stall(&mut self, why: impl Into<String>)
pub fn stall(&mut self, why: impl Into<String>)
Record an attempt that failed for a reason the task is not responsible for - the agent CLIs ran out of quota and the judging panel collapsed.
This refunds the attempt on purpose. A quota window closing at 4am must
not spend the backlog’s retry budget: the operator would come back to a
queue of held tasks that were never actually judged, and would have to
release every one by hand to find out which had a real problem. The task
goes back to Failed, which the loop retries, so a reset quota picks the
work up where it stopped.
Sourcepub fn hold(&mut self, reason: Option<String>)
pub fn hold(&mut self, reason: Option<String>)
Take this task out of the loop’s reach without deleting it.
reason replaces whatever was recorded before when it is given.
Passing None - the loop’s own holds do this - leaves any existing
reason alone, so a machine-initiated hold cannot erase what a human
wrote down about a previous one.
Sourcepub fn set_priority(&mut self, priority: i32) -> Result<()>
pub fn set_priority(&mut self, priority: i32) -> Result<()>
Change how urgently this task should run next.
Refused once the task is running: priority only feeds the sort
Queue::next_runnable does over tasks waiting to be claimed, and a
running task has already left that pool. Accepting the write anyway
would look like it worked while changing nothing until - and unless -
this attempt fails and the task becomes runnable again, which is a
surprise the phone should not hand back as a success.
Sourcepub fn edit(&mut self, title: String, instruction: String) -> Result<()>
pub fn edit(&mut self, title: String, instruction: String) -> Result<()>
Replace this task’s title and instruction wholesale.
Restricted to queued and held. A running task’s instruction has
already been handed to the graph, so a run in flight and the file on
disk must not be allowed to disagree about what was asked; a done or
failed task is a record of what actually happened and editing it
after the fact would falsify that record. id, created_at,
source, and runs are left untouched on purpose - an edit stands in
for “delete and refile”, and keeping the id, the timestamp, the
attribution, and the run history is the entire reason it exists
instead.
Sourcepub fn handed_off(&mut self, why: impl Into<String>)
pub fn handed_off(&mut self, why: impl Into<String>)
Record a run that produced a pull request without merging it.
The task is held rather than retried, and it costs no further attempt either way. The work the task asked for exists: it is sitting on a branch, in a pull request, waiting for CI or for a person. Retrying would spend the whole competition budget a second time and then race a second branch against the pull request the first one opened - which is exactly what happened to run 01c2, whose finished and green pull request was re-competed from scratch four seconds after it opened.
A pull request nobody merged is a request for a person, not a failure.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Task
impl<'de> Deserialize<'de> for Task
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for Task
impl RefUnwindSafe for Task
impl Send for Task
impl Sync for Task
impl Unpin for Task
impl UnsafeUnpin for Task
impl UnwindSafe for Task
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more