Skip to main content

RunStore

Trait RunStore 

Source
pub trait RunStore: Send + Sync {
Show 15 methods // Required methods fn name(&self) -> &str; fn create<'life0, 'async_trait>( &'life0 self, record: RunRecord, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait; fn get<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, ) -> Pin<Box<dyn Future<Output = Result<RunRecord, RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn list_by_task<'life0, 'life1, 'async_trait>( &'life0 self, task_id: &'life1 TaskId, ) -> Pin<Box<dyn Future<Output = Result<Vec<RunRecord>, RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn append_step_entry<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, entry: StepEntry, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn append_degradation<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, entry: DegradationEntry, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn update_status<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, status: RunStatus, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn try_transition<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, from: RunStatus, to: RunStatus, ) -> Pin<Box<dyn Future<Output = Result<bool, RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn acquire_assignee<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, id: &'life1 RunId, slot: &'life2 str, op: &'life3 str, desc: &'life4 str, ) -> Pin<Box<dyn Future<Output = Result<(u64, Option<Assignee>), RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait; fn vacate_assignee<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, id: &'life1 RunId, slot: &'life2 str, expected_gen: u64, ) -> Pin<Box<dyn Future<Output = Result<VacateOutcome, RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait; fn set_result<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, result_ref: Value, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn set_input_json<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, input_json: String, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn list_running<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<RunRecord>, RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait; fn list<'life0, 'life1, 'async_trait>( &'life0 self, filter: &'life1 RunListFilter, ) -> Pin<Box<dyn Future<Output = Result<Vec<RunRecord>, RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait; fn delete<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait;
}
Expand description

Persistence interface for Run records — one kick of a Task, in the issue #13 ID hierarchy.

Required Methods§

Source

fn name(&self) -> &str

Backend name — for diagnostics/logging.

Source

fn create<'life0, 'async_trait>( &'life0 self, record: RunRecord, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Create a new Run row. Returns Duplicate if record.id is already stored.

This is the one door into the store that carries a caller-built RunRecord, so it is where the assignment axis is checked rather than assumed: a record whose current holds a generation above next_generation is refused with RunStoreError::AssigneeGenerationAhead (A2, see RunRecord::validate_assignment_generations). Implementations must run that check before persisting anything. The rest of the record — step_entries, status, timestamps — is still trusted as given.

Source

fn get<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, ) -> Pin<Box<dyn Future<Output = Result<RunRecord, RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Fetch a Run by id.

Source

fn list_by_task<'life0, 'life1, 'async_trait>( &'life0 self, task_id: &'life1 TaskId, ) -> Pin<Box<dyn Future<Output = Result<Vec<RunRecord>, RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

List every Run kicked from task_id, ascending by created_at (oldest kick first).

Source

fn append_step_entry<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, entry: StepEntry, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Append one step-trace entry to a Run’s step_entries, bumping updated_at to now.

Source

fn append_degradation<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, entry: DegradationEntry, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Append one worker-reported degradation to a Run’s degradations (GH #32), bumping updated_at to now. Independent of Self::append_step_entry — degradations never flow through step OUTPUT/fold.

Source

fn update_status<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, status: RunStatus, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Update a Run’s status, bumping updated_at to now.

Source

fn try_transition<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, from: RunStatus, to: RunStatus, ) -> Pin<Box<dyn Future<Output = Result<bool, RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Atomically transition a Run’s status from from to to, bumping updated_at to now — the compare-and-set primitive the resume path (POST /v1/runs/:id/resume) uses to guard against a double resume racing the same Interrupted Run into Running twice.

Returns Ok(true) when a row with this id AND current status from was found and flipped to to; Ok(false) when the row’s current status was not from (a concurrent transition already won, or the Run is absent). Never a hard error for the status-mismatch / absent case — the boolean is the caller’s race signal.

Source

fn acquire_assignee<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>( &'life0 self, id: &'life1 RunId, slot: &'life2 str, op: &'life3 str, desc: &'life4 str, ) -> Pin<Box<dyn Future<Output = Result<(u64, Option<Assignee>), RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, 'life3: 'async_trait, 'life4: 'async_trait,

Assign this Run’s slot to op — the model’s Assign event (§4.3).

slot is the Blueprint-declared Operator seat (operator_ref) the assignment applies to; only that key of RunRecord::current is touched, so assigning one seat never disturbs another’s holder.

Bumps the Run’s generation counter G by one and stamps the new value onto a freshly minted Assignee, which replaces current[slot] (A4 / Q3: the previously stored Assignee is returned untouched, never rewritten in place). G is Run-wide, so this advances the same counter every other slot’s events advance. updated_at is bumped to now.

A8: this succeeds regardless of who holds the slot — a live holder is displaced (last writer wins); there is no exclusion and no rejection path for a contended slot. The only refusals are A9 (an empty or whitespace-only desc returns RunStoreError::AssigneeDescRequired) and an empty slot (returns RunStoreError::AssigneeSlotRequired); an unknown id returns RunStoreError::NotFound.

The read of G and the write of both columns happen atomically, so two concurrent acquires can never read the same G and hand out a duplicate generation — including when they name different slots.

Returns (new generation, the holder this call displaced from this slot) — the caller needs both to tell whether it took over from someone and under which generation it now dispatches.

Source

fn vacate_assignee<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, id: &'life1 RunId, slot: &'life2 str, expected_gen: u64, ) -> Pin<Box<dyn Future<Output = Result<VacateOutcome, RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait,

Release the holder of this Run’s slot — the model’s Vacant event (§4.3) — but only while that seat still holds the generation the caller observed. Other slots keep their holders.

expected_gen is the gen of the Assignee the caller read and decided about. The comparison and the write happen in one critical section (the same transaction / lock acquire_assignee uses), so a concurrent acquire either lands before the check — and the release becomes a no-op — or after the write, which is an ordinary A8 takeover of an already-Vacant seat. There is no window in which a stale reader deletes a newer holder. See VacateOutcome for why the generation has to travel with the call at all; this is the only release verb, because both production callers (A7 at AssigneeRouter::execute and O8’s cascade) are stale readers, and an unconditional sibling would exist only to be picked by mistake.

A4: a release that actually happens bumps the Run-wide generation counter exactly like Assign does; it just mints no Assignee. A subsequent Self::acquire_assignee — on this slot or any other — therefore continues from the bumped value rather than reusing the generation the released holder had. updated_at is bumped to now. A VacateOutcome::Stale result is not an assignment event and writes nothing at all: the counter counts events, and a release that did not release is not one.

An already-Vacant slot therefore answers Stale { current: None } rather than burning a generation — no generation can match an absent holder. An empty slot returns RunStoreError::AssigneeSlotRequired and an unknown id returns RunStoreError::NotFound.

Source

fn set_result<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, result_ref: Value, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Set a Run’s terminal result_ref, bumping updated_at to now.

Source

fn set_input_json<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, input_json: String, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Replace the opaque launch snapshot after pre-dispatch binding has enriched it (for example with immutable bound_agents).

Source

fn list_running<'life0, 'async_trait>( &'life0 self, ) -> Pin<Box<dyn Future<Output = Result<Vec<RunRecord>, RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

List every Run currently Running (issue #35 ST2 boot sweep + ST4 occupancy check reuse this). No ordering guarantee.

Source

fn list<'life0, 'life1, 'async_trait>( &'life0 self, filter: &'life1 RunListFilter, ) -> Pin<Box<dyn Future<Output = Result<Vec<RunRecord>, RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

List Runs matching filter, newest-first (created_at descending) — the GET /v1/runs collection read.

Source

fn delete<'life0, 'life1, 'async_trait>( &'life0 self, id: &'life1 RunId, ) -> Pin<Box<dyn Future<Output = Result<(), RunStoreError>> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait,

Delete a Run row (the DELETE /v1/runs/:id retention operation). The caller is responsible for pruning the sibling trace stream (crate::store::trace::RunTraceStore::delete_run) — the two stores are deliberately uncoupled at the trait level.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§