Skip to main content

Graph

Struct Graph 

Source
pub struct Graph {
Show 22 fields pub candidates: usize, pub judges: usize, pub deliberate_rounds: usize, pub reviewers: usize, pub review_rounds: usize, pub max_parallel: usize, pub language: String, pub sessions: bool, pub timeout_implement: u64, pub timeout_judge: u64, pub timeout_review: u64, pub timeout_verify: Option<u64>, pub timeout_fix: u64, pub timeout_talk: u64, pub retries: usize, pub worktree_root: Option<PathBuf>, pub land: bool, pub land_rounds: usize, pub land_approval: bool, pub answer_timeout: u64, pub incomplete_review: IncompleteReviewPolicy, pub e2e_every_round: bool,
}
Expand description

Graph shape and limits.

Fields§

§candidates: usize

Parallel implementations of the same task. One by default.

Competition is the thing magi is for, and it is still here - it is just no longer what every task buys without being asked. Three days and 13 runs on this repository, which is the workload these numbers are drawn from:

  • 0 of 13 competed runs reached a merge. Everything that landed in that window went through magi review - the cheap half, no competition - and passed on the first try.
  • The judges’ first choices split 73% of the time (8 of 11 tallies). Candidates that close together make the ranking a weak signal for what it costs to produce.
  • One run’s own breakdown: implement 60min, judge 40min, fix 40min, review 28min, verify 5min - and verify is the node that caught a defect every reviewer had passed as clean. The cheapest step is the one that earns its place every time.

It is not worthless: oc won 3 of those tallies against sonnet, so a single-seat default would have shipped the worse implementation in roughly a quarter of them. That is exactly why this is a default and not a removal - magi run --candidates N and a per-task seat count are how a task that deserves a competition gets one.

A single-candidate run needs no special case: Runner::review’s doc records that execute already degrades to implement -> review -> gate -> merge, because judge skips a one-candidate field, deliberate has no two first choices to reconcile and vote returns early.

§judges: usize

Independent judges.

§deliberate_rounds: usize

Deliberation rounds when the judges’ first choices disagree.

§reviewers: usize

Reviewers per review round. Three by default - the smallest panel a lens cycle (see crate::prompt::Lens) covers exactly once, so the default panel reads the patch for spec compliance, regressions, and simplicity without repeating an angle. Review is also the one stage Self::candidates’s doc describes as running on every task regardless of competition, which is what makes a panel worth its cost here even though candidates itself defaults to one.

§review_rounds: usize

Maximum review+fix rounds before the run is declared blocked.

§max_parallel: usize

Maximum agent processes running at once.

§language: String

Language for the prose the agents write (en / ja / any language name).

§sessions: bool

Keep one CLI conversation per seat, so a judge remembers its own argument across deliberation rounds and the fixer remembers its own implementation across review rounds.

Sessions are scoped to a seat, never to an agent id: the same model sitting as implementer and as judge gets two unrelated conversations, which is what keeps blind judging blind.

§timeout_implement: u64

Per-node timeouts, seconds.

§timeout_judge: u64

Per-node timeouts, seconds.

§timeout_review: u64

Per-node timeouts, seconds.

§timeout_verify: Option<u64>

Timeout for verify.e2e and verify.gate, seconds. Separate from Self::timeout_review so shrinking a reviewer’s budget cannot silently shrink a real-machine command’s budget too — the two used to share timeout_review, and turning a slow reviewer down cut the timeout cargo test --all-targets runs under along with it. When omitted, preserves legacy configurations by using Self::timeout_review. Set an explicit value to make verification independent of later review-seat budget changes.

§timeout_fix: u64

Per-node timeouts, seconds.

§timeout_talk: u64

Wall-clock limit for one turn of crate::talk’s standing conversation, seconds.

An hour: the operator is not watching this turn resolve in real time, so the budget can match what the work - reading files, running commands, checking their output - actually needs rather than what a person waiting on a phone can tolerate.

§retries: usize

Retries for an agent invocation that fails or returns nothing usable.

§worktree_root: Option<PathBuf>

Root for candidate / judge worktrees. Defaults to ~/wt/magi.

§land: bool

After the pull request is open, keep going: watch its checks and reviews, run a fix round when they are unhappy, and ask to merge.

On, because stopping at an open pull request left the operator doing the watching by hand - six times in the session this was built in - and that is the work the loop exists to take. It only engages for merge = "pr"; every other merge mode ends the run as before.

Turning this on does not hand magi the merge button: Graph::land_approval is on too, and nothing merges without an explicit answer. Setting both to their non-defaults is the only way to get an unattended merge, and it has to be chosen twice.

§land_rounds: usize

Land rounds - watch, fix, push - before the run is left for a human.

§land_approval: bool

Ask the owner before merging, showing what is about to land.

On, and it is what makes land safe to have on: the question carries a rendered panel - the diffstat, the patch, the checks, the review comments that were addressed, and the subject the squash will use - so the decision is made on evidence rather than on trust, from wherever the operator happens to be.

Silence is a hold. An unanswered approval never merges, and neither does any answer other than the word merge.

§answer_timeout: u64

How long to wait for an owner to answer a question before the run is abandoned, seconds. A parked run costs nothing, so this is generous; it exists so a forgotten question cannot pin a worktree forever.

§incomplete_review: IncompleteReviewPolicy

What a round does when one or more reviewer seats never answered (timeout, crash, unparsable output).

§e2e_every_round: bool

Run verify.e2e on every round, even one that already has blocking findings and another round left to try.

Off by default: a round with a blocking finding and rounds still left is going back to the fixer regardless of what verify.e2e says, so running it first only spends the round’s slowest step (minutes, on a Rust repo’s cargo test --all-targets) on a head about to be rewritten anyway. verify.e2e still runs once a round has no blocking findings left (a round cannot go clean without it) and the final verify.gate always runs on the actual tree that would land — deferring is about when e2e runs mid-loop, never about skipping it.

Set this to restore the old every-round diagnostic behaviour: e2e output from a round that still has blocking findings is occasionally useful on its own (a runtime failure a reviewer’s panel would not have caught by reading), and this is the way back to seeing it every round instead of only once the panel has nothing left to flag.

Implementations§

Source§

impl Graph

Source

pub fn verify_timeout(&self) -> u64

Effective machine-command budget. Older configuration files had only timeout_review, which also governed verification, so absence is a compatibility fallback rather than a new 1200-second default.

Trait Implementations§

Source§

impl Clone for Graph

Source§

fn clone(&self) -> Graph

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Graph

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Graph

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for Graph
where Graph: Default,

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for Graph

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

§

impl Freeze for Graph

§

impl RefUnwindSafe for Graph

§

impl Send for Graph

§

impl Sync for Graph

§

impl Unpin for Graph

§

impl UnsafeUnpin for Graph

§

impl UnwindSafe for Graph

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more