Skip to main content

Questions

Struct Questions 

Source
pub struct Questions { /* private fields */ }
Expand description

A question store on disk.

Implementations§

Source§

impl Questions

Source

pub fn open() -> Self

The operator’s questions, <home>/questions.

Source

pub fn at(root: PathBuf) -> Self

A store at an explicit root. Tests use this, which is why none of them need the operator’s real home.

Source

pub fn root(&self) -> &Path

Directory holding the question files.

Source

pub fn path_of(&self, id: &str) -> PathBuf

Path for one question id.

Source

pub fn panel_dir(&self, id: &str) -> PathBuf

Directory holding one question’s panel, <root>/<id>.panel.

Source

pub fn put_panel( &self, q: &mut Question, html: &str, assets: &[PathBuf], ) -> Result<()>

Store a panel: the html, plus assets copied in under their base names. Updates q.panel and q.assets; the caller then puts the question, or the record on disk will deny having a panel that exists.

The assets are copied, not referenced. An agent authors its panel inside a candidate worktree and points at files there, and magi fold deletes those worktrees; a question is the permanent record of a decision the owner took, so a panel that referenced its own images would render as broken boxes exactly when someone went back to ask why the decision was made. Copying follows symlinks - std::fs::copy does, and so does the std::fs::metadata the size is measured with, so the bytes counted and the bytes written are the same target file’s - which is the intent: storing a link would leave the panel pointing at the worktree again, one indirection further away.

Everything that can be rejected is rejected before the first byte is written, and the panel is then assembled in a scratch directory and swapped in. So a refusal leaves the previous panel intact, and a success replaces it wholesale rather than merging: a re-asked question showing one attempt’s diff next to another attempt’s table would be a panel neither agent ever wrote.

Source

pub fn panel_html(&self, id: &str) -> Option<String>

The panel’s html, or None when the question has no panel.

None rather than an error for a missing panel because the caller is a web handler whose answer is 404 either way, and an unreadable panel is not a reason to fail the question it belongs to.

Source

pub fn panel_asset(&self, id: &str, name: &str) -> Result<Option<Vec<u8>>>

One file from a panel. Ok(None) is “no such file”; Err is “that is not a name a panel file can have”.

Rejects a name failing valid_asset_name before touching the filesystem, which is the whole point of the second check: the name arrives from a URL, the directory is on disk where any process could have dropped a file, and <root>/<id>.panel/../../id_rsa is a path the operating system would resolve perfectly happily. The two callers’ distinct outcomes - 400 for a name, 404 for a file - are why this is Result<Option<_>> rather than one flattened Option.

Source

pub fn drop_panel(&self, id: &str) -> Result<()>

Delete a question’s panel, and any scratch a killed put_panel left.

Succeeds when there is nothing to delete, so a caller cleaning up does not have to know whether a panel was ever written. The question record is not touched: the caller clears panel and assets and puts it, in the same order as everywhere else here.

Source

pub fn put(&self, q: &mut Question) -> Result<()>

Write a question, atomically, so a process killed mid-write leaves the previous state readable rather than a truncated file that would strand the run waiting on it.

Source

pub fn get(&self, id: &str) -> Result<Question>

Load a question by id or unambiguous id prefix.

Source

pub fn list(&self) -> Vec<Question>

Every question on disk: open first, then newest first.

Open first because that ordering is the product - the list exists to show the operator what has stopped, and an answered question is history underneath it. Unreadable files are skipped rather than fatal: one corrupt question must not take the web UI down, and must certainly not hide the open question the operator was looking for.

Source

pub fn open_for(&self, run: &str) -> Vec<Question>

Open questions belonging to one run, newest first.

Used to decide whether a parked run can be resumed: while this is non-empty, nothing about the run has changed and no agent should be spawned for it.

Source

pub fn abandon_for_run(&self, run: &str, why: &str) -> Result<usize>

Abandon every open question belonging to a run, and report how many.

Called when a run’s record is deleted. The agent that asked died with the run, so there is nobody left to hand an answer to, and a question left open would keep asking the operator for a decision that can no longer be delivered - the phone showed exactly that: “auth.rs というファ イルが見つかりません” with two buttons, for a run whose directory had been gone for two hours.

Abandoned rather than deleted, because Question::abandon already means “this can no longer be answered” and the record of having asked is worth keeping. Answered questions are left exactly as they are.

Source

pub fn settle_run(&self, run: &str, status: RunStatus) -> Result<usize>

Abandon a run’s open questions once status says the run is not coming back, worded with what it actually became.

The run-deleted case above and this one are the same fact - nobody is left to read an answer - reached by two different doors. This is the one for a run that finished on its own: merged, reached Ready with nothing left to do, or failed outright with no established point to resume from. Those are exactly the statuses RunStatus::resumable excludes, and that is the line this draws too - deliberately not RunStatus::done, which also counts Blocked and Stalled as over. Both of those can still be picked back up with the candidates, the review round and the seat sessions already on disk, so a question asked mid-round may yet get a real answer from a real resume, and folding it here would be exactly the mistake this function exists to avoid on the other side - answering back into a run that no longer exists to read it.

A no-op, not an error, when status is still resumable or when there was nothing open to begin with - callers reach this from more than one place a run can settle, and a second call finding nothing left to abandon is the expected case, not a bug.

Source

pub fn resolve_id(&self, prefix: &str) -> Result<String>

Expand an id prefix to exactly one question id. The short id the phone and the reports show is a suffix, so that is accepted too.

Source

pub fn revision(&self) -> u64

Newest modification time in the store, in milliseconds, for change detection. The web UI compares this instead of re-reading every question, so an idle phone on a slow link costs one stat per file.

Source

pub fn count_open(&self) -> usize

How many questions are open, whichever side of the conversation is holding the ball right now. Ten turns of back and forth between the owner and the agent are still one open question - see Question::say - so this does not drop while a reply is in flight. Self::count_needs_owner is the number that does.

Source

pub fn count_needs_owner(&self) -> usize

How many open questions actually need the owner right now: open, and not Question::waiting_on_agent.

This is the number a notification channel owes - the ask bar, the nav badge, the document title - because those exist to say “something needs you”, and a question sitting in magi ask --thread limbo does not. count_open stays as it is for Self::open_for’s callers, where a round trip must not look like the run resumed.

Trait Implementations§

Source§

impl Clone for Questions

Source§

fn clone(&self) -> Questions

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 Questions

Source§

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

Formats the value using the given formatter. Read more

Auto Trait Implementations§

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> 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