pub struct Store { /* private fields */ }Expand description
Owns the SQLite database. All writes go through this type; task state in
particular is only ever changed by the transition API in transition.rs.
Implementations§
Source§impl Store
impl Store
Sourcepub fn candidates(&self) -> Result<Vec<Candidate>>
pub fn candidates(&self) -> Result<Vec<Candidate>>
Scheduler input: every task in a scored state, joined with its project, excluding weight-0 (parked) and archived projects entirely (§5/§7). The open-dependent count arrives as one grouped join, not a lookup per row.
Sourcepub fn explain(&self, task_id: i64) -> Result<ScoreBreakdown>
pub fn explain(&self, task_id: i64) -> Result<ScoreBreakdown>
Score decomposition for any single task, whatever its state — the
TUI popup today, voro explain <task> later.
Sourcepub fn proposed_count(&self) -> Result<i64>
pub fn proposed_count(&self) -> Result<i64>
Count of untriaged tasks. Parked (weight-0) and archived projects are hidden here too.
Sourcepub fn state_counts(&self) -> Result<StateCounts>
pub fn state_counts(&self) -> Result<StateCounts>
Task counts by state for the header indicator (DESIGN.md §12), so a backlog stays felt even when a low-scoring row falls past the queue’s cap (§7). Parked (weight-0) and archived projects are excluded.
Source§impl Store
Drop every row, leaving the schema in place — the reset behind
voro seed --force. The CLI is what confines this to the dev store.
impl Store
Drop every row, leaving the schema in place — the reset behind
voro seed --force. The CLI is what confines this to the dev store.
pub fn truncate_all(&mut self) -> Result<()>
Source§impl Store
impl Store
pub fn open(path: &Path) -> Result<Store>
Sourcepub fn open_migrate(path: &Path, consent: &str) -> Result<Store>
pub fn open_migrate(path: &Path, consent: &str) -> Result<Store>
Open with consent to migrate a protected store (§5): the TUI’s launch
prompt and voro migrate call this after a human has answered, or with
--yes standing in for one. consent says how the consent was given
and is recorded in the journal’s applied_by. On an unprotected store
it changes nothing — migration there never needed asking.
pub fn open_in_memory() -> Result<Store>
Sourcepub fn production_db_path() -> PathBuf
pub fn production_db_path() -> PathBuf
The operator’s store (DESIGN.md §5), at a path that does not vary with
how the running binary was built. Dispatch renders --db against it,
and voro seed refuses it.
Sourcepub fn dev_db_path() -> PathBuf
pub fn dev_db_path() -> PathBuf
The store a build out of a target/ directory opens instead (DESIGN.md
§5). Seeded on first open and disposable: voro seed --force rebuilds
it, and deleting it costs nothing.
Sourcepub fn backup_dir_for(path: &Path) -> PathBuf
pub fn backup_dir_for(path: &Path) -> PathBuf
Where snapshots taken before a migration land: beside the database they
protect, so a store opened with --db keeps its own history.
Sourcepub fn is_dev_build() -> bool
pub fn is_dev_build() -> bool
True when this binary was run out of a Cargo target/ directory rather
than installed. It picks the default store and bounds nothing:
cargo install --path builds a working checkout, unreleased migrations
and all, into an ordinary install location, where this reads as an
install. The journal and the counter (§5) are what protect the schema.
Sourcepub fn default_db_path() -> PathBuf
pub fn default_db_path() -> PathBuf
The store a bare voro opens: the dev one for a dev build, the
operator’s otherwise.
Sourcepub fn schema_version(&self) -> Result<usize>
pub fn schema_version(&self) -> Result<usize>
The store’s user_version. An open store always reads as the count of
migrations its build carries; voro migrate reports it when there was
nothing to apply.
Sourcepub fn data_version(&self) -> Result<i64>
pub fn data_version(&self) -> Result<i64>
SQLite’s PRAGMA data_version, which increments whenever another
connection commits a change to the database. The value is stable across
commits made on this connection, so a caller can poll it to detect
external writes without reacting to its own mutations.
Sourcepub fn create_project(&mut self, name: &str, path: &str) -> Result<Project>
pub fn create_project(&mut self, name: &str, path: &str) -> Result<Project>
Create a project and its default repo in one transaction, so a project
with no checkout is never observable (DESIGN.md §5). The repo is named
after the project; voro repo renames nothing, but repo add puts
further checkouts beside it.
pub fn project(&self, id: i64) -> Result<Project>
pub fn projects(&self) -> Result<Vec<Project>>
pub fn set_weight(&mut self, project_id: i64, weight: i64) -> Result<()>
Sourcepub fn set_viewer(
&mut self,
project_id: i64,
viewer: Option<&str>,
) -> Result<Project>
pub fn set_viewer( &mut self, project_id: i64, viewer: Option<&str>, ) -> Result<Project>
Name the voro.toml viewer this project’s local diffs open in
(DESIGN.md §8/§11a). None stores NULL — no viewer named, so open
falls back to the config’s default viewer.
Sourcepub fn set_archived(
&mut self,
project_id: i64,
archived: bool,
) -> Result<Project>
pub fn set_archived( &mut self, project_id: i64, archived: bool, ) -> Result<Project>
Archive or unarchive a project (DESIGN.md §5). Archiving hides the project and all of its tasks from the cockpit views; the tasks themselves are not touched — no state change, no event — so unarchiving restores the pre-archive view exactly. Refuses a no-op so a typo’d second archive is heard rather than silently absorbed.
Sourcepub fn rename_project(&mut self, project_id: i64, name: &str) -> Result<Project>
pub fn rename_project(&mut self, project_id: i64, name: &str) -> Result<Project>
Tasks reference a project by id, not name, so renaming is a pure label change — no task or dependency is touched.
Sourcepub fn set_default_repo_path(
&mut self,
project_id: i64,
path: &str,
) -> Result<Repo>
pub fn set_default_repo_path( &mut self, project_id: i64, path: &str, ) -> Result<Repo>
Re-point a project’s default repo. This is what voro project path
and the projects screen’s path field edit — the single-repo spelling of
repo path, kept because a one-repo project is still the common case.
Sourcepub fn delete_project(&mut self, project_id: i64) -> Result<()>
pub fn delete_project(&mut self, project_id: i64) -> Result<()>
Delete a project outright — only safe when it has no tasks, since tasks reference their project by id and deleting would orphan history. A project with tasks in any state refuses; weight 0 snoozes without losing history. Its repos go with it: no task references them, so nothing is orphaned.
Sourcepub fn repos(&self, project_id: i64) -> Result<Vec<Repo>>
pub fn repos(&self, project_id: i64) -> Result<Vec<Repo>>
A project’s repos, default first, then by name.
pub fn repo(&self, id: i64) -> Result<Repo>
Sourcepub fn repo_by_name(&self, project_id: i64, name: &str) -> Result<Repo>
pub fn repo_by_name(&self, project_id: i64, name: &str) -> Result<Repo>
A project’s repo by name. An unknown name errors listing the project’s repos, so a filing agent gets a correction rather than a wrong checkout.
pub fn default_repo(&self, project_id: i64) -> Result<Repo>
Sourcepub fn repo_for_task(&self, task: &Task) -> Result<Repo>
pub fn repo_for_task(&self, task: &Task) -> Result<Repo>
The checkout a task’s work runs in (DESIGN.md §8): its own repo when it
names one, else its project’s default. The single resolution point —
dispatch, pr/open, worktree cleanup, and import all come here
rather than reading repo_id themselves.
Sourcepub fn add_repo(
&mut self,
project_id: i64,
name: &str,
path: &str,
) -> Result<Repo>
pub fn add_repo( &mut self, project_id: i64, name: &str, path: &str, ) -> Result<Repo>
Add a repo to a project. The first repo of a project is made by
create_project, so one added here is never the default; set_default
promotes it.
pub fn set_repo_path(&mut self, repo_id: i64, path: &str) -> Result<Repo>
Sourcepub fn set_default_repo(&mut self, repo_id: i64) -> Result<Repo>
pub fn set_default_repo(&mut self, repo_id: i64) -> Result<Repo>
Make a repo its project’s default. Clearing the old default and setting the new one share a transaction, because the partial unique index would otherwise refuse the intermediate state.
Sourcepub fn delete_repo(&mut self, repo_id: i64) -> Result<()>
pub fn delete_repo(&mut self, repo_id: i64) -> Result<()>
Remove a repo, refusing the three ways it would leave the store inconsistent: the project’s last repo (a project always has a checkout), its default while others remain (set a new one first), and one any task still names (re-point those tasks first).
Sourcepub fn set_task_repo(
&mut self,
task_id: i64,
repo_id: Option<i64>,
) -> Result<Task>
pub fn set_task_repo( &mut self, task_id: i64, repo_id: Option<i64>, ) -> Result<Task>
Re-point a task at a repo of its own project, or back at the default
with None. A repo belonging to another project is refused — a task’s
checkout is chosen from its project’s repos, not from every repo.
Sourcepub fn create_doc(
&mut self,
project_id: i64,
repo_id: Option<i64>,
location: &str,
title: Option<&str>,
) -> Result<Doc>
pub fn create_doc( &mut self, project_id: i64, repo_id: Option<i64>, location: &str, title: Option<&str>, ) -> Result<Doc>
Register a document against a project. location is a checkout-relative
path, an absolute path, or a URL; an absolute path that lies inside one
of the project’s checkouts is stored relative to it (DESIGN.md §5), so
the link survives the checkout moving. repo names which checkout a
relative path resolves against, None meaning the project’s default.
pub fn doc(&self, id: i64) -> Result<Doc>
Sourcepub fn docs(&self, project_id: i64) -> Result<Vec<Doc>>
pub fn docs(&self, project_id: i64) -> Result<Vec<Doc>>
A project’s documents, oldest first — registration order is the closest thing a plan library has to a meaningful one.
pub fn all_docs(&self) -> Result<Vec<Doc>>
Sourcepub fn docs_at(&self, location: &str) -> Result<Vec<Doc>>
pub fn docs_at(&self, location: &str) -> Result<Vec<Doc>>
Every document with the given location, across projects — what a --doc
flag naming a path rather than an id matches. More than one match is
returned rather than resolved, so the caller can say which ids collided.
Sourcepub fn resolve_doc(&self, doc: &Doc) -> Result<String>
pub fn resolve_doc(&self, doc: &Doc) -> Result<String>
Where a document actually is: a URL or absolute path verbatim, and a relative one joined onto its checkout. The single resolution point — dispatch and every renderer come here rather than joining paths itself.
Sourcepub fn delete_doc(&mut self, doc_id: i64) -> Result<Vec<i64>>
pub fn delete_doc(&mut self, doc_id: i64) -> Result<Vec<i64>>
Remove a document and every task link to it, in one transaction. Unlike a repo, a doc is navigational — nothing resolves to nothing when it goes — so this unlinks rather than refusing, and returns the tasks it freed so the caller can say how far the removal reached.
Sourcepub fn link_doc(&mut self, task_id: i64, doc_id: i64) -> Result<bool>
pub fn link_doc(&mut self, task_id: i64, doc_id: i64) -> Result<bool>
Link a task to a document. Returns whether the edge was new, so a repeated link reads as a no-op rather than an error — and logs the link on the task’s own event trail only when something changed.
pub fn unlink_doc(&mut self, task_id: i64, doc_id: i64) -> Result<bool>
Sourcepub fn set_task_docs(
&mut self,
task_id: i64,
doc_ids: &[i64],
) -> Result<Vec<Doc>>
pub fn set_task_docs( &mut self, task_id: i64, doc_ids: &[i64], ) -> Result<Vec<Doc>>
Replace a task’s whole document list — what set --doc writes, matching
--blocked-by’s replace semantics so the flag can remove a link as well
as add one. Each added and dropped edge is logged individually.
Sourcepub fn docs_for_task(&self, task_id: i64) -> Result<Vec<Doc>>
pub fn docs_for_task(&self, task_id: i64) -> Result<Vec<Doc>>
The documents a task cites, in registration order.
Sourcepub fn docs_by_task(&self) -> Result<HashMap<i64, Vec<Doc>>>
pub fn docs_by_task(&self) -> Result<HashMap<i64, Vec<Doc>>>
Every document link keyed by task id, loaded whole — what the TUI reads once per refresh so the render path never queries the store, the same shape as the dependency maps.
Sourcepub fn tasks_for_doc(&self, doc_id: i64) -> Result<Vec<Task>>
pub fn tasks_for_doc(&self, doc_id: i64) -> Result<Vec<Task>>
The tasks derived from a document — the “which tasks came from this plan?” query, in id order so a plan’s rollout reads chronologically.
pub fn create_task(&mut self, new: NewTask) -> Result<Task>
pub fn task(&self, id: i64) -> Result<Task>
pub fn tasks(&self) -> Result<Vec<Task>>
pub fn update_task(&mut self, id: i64, edit: TaskEdit) -> Result<Task>
Sourcepub fn set_priority(&mut self, id: i64, priority: Priority) -> Result<Task>
pub fn set_priority(&mut self, id: i64, priority: Priority) -> Result<Task>
Re-prioritise a task in isolation (DESIGN.md §7). Unlike update_task
this touches only priority, and it logs the change. Task state is left
untouched.
Sourcepub fn set_deep(&mut self, id: i64, deep: bool) -> Result<Task>
pub fn set_deep(&mut self, id: i64, deep: bool) -> Result<Task>
Flag a task as warranting the agent’s strongest model, or clear the flag
(DESIGN.md §8). Like set_priority this touches one field and logs the
change, so the TUI’s toggle needs no full edit; task state is untouched.
Refused on a human task, which is never dispatched and so has no model.
Sourcepub fn set_pr(&mut self, id: i64, pr_url: Option<&str>) -> Result<Task>
pub fn set_pr(&mut self, id: i64, pr_url: Option<&str>) -> Result<Task>
Track (or, with None, untrack) a GitHub PR on a task (DESIGN.md §11c).
The URL is stored verbatim — validation is the caller’s job — and the
change is logged. Leaves task state untouched.
Sourcepub fn set_branch(&mut self, id: i64, branch: Option<&str>) -> Result<Task>
pub fn set_branch(&mut self, id: i64, branch: Option<&str>) -> Result<Task>
Record (or, with None, clear) the git branch a task’s work lives on —
the intended name a human sets for dispatch to inject, or the name an
agent reports through voro done --branch. Stored verbatim (Voro never
runs git) and logged; task state is left untouched.
Sourcepub fn set_summary(&mut self, id: i64, summary: &str) -> Result<Task>
pub fn set_summary(&mut self, id: i64, summary: &str) -> Result<Task>
Set or replace a task’s completion summary outside done (DESIGN.md §8):
append a summary event, which latest_summary supersedes with, so the
PR body, detail view, and incomplete-report flag all pick up the newest.
This amends a stale PR body or supplies a missing [incomplete report]
summary without a reject → re-done round trip. Allowed only on a
running or review task; it never touches tasks.state.
Sourcepub fn latest_refine_outcome(
&self,
task_id: i64,
) -> Result<Option<RefineOutcome>>
pub fn latest_refine_outcome( &self, task_id: i64, ) -> Result<Option<RefineOutcome>>
How the newest concluded refine round on a task ended (DESIGN.md §6),
read off the refine event the refining → proposed transition logs.
None for a task no round has ever concluded on. This is what the two
row markers are derived from, so a proposal says which of “reworked” and
“the rewrite died” it is rather than leaving the operator to notice an
absence.
Sourcepub fn refined_flag(&self, task_id: i64) -> Result<bool>
pub fn refined_flag(&self, task_id: i64) -> Result<bool>
Whether task_id is a proposed task whose last refine round rewrote
its body (DESIGN.md §6) — what renders the ↻ refined marker. Gated on
proposed, so triaging the task clears it, and on the concluded round,
so a proposal is only marked once the improved body exists. Derived
fresh, never stored.
Sourcepub fn refine_failed_flag(&self, task_id: i64) -> Result<bool>
pub fn refine_failed_flag(&self, task_id: i64) -> Result<bool>
The other half of refined_flag: a proposed task whose last refine
round died without applying anything, which renders the ⚠ refine failed marker. Same lifecycle — shown while proposed, cleared by
triage — because a failed refine must be visibly different from a
proposal nobody has refined.
Sourcepub fn correct_late_refine(&mut self, task_id: i64) -> Result<bool>
pub fn correct_late_refine(&mut self, task_id: i64) -> Result<bool>
Correct the outcome of a round that concluded failed but whose rewrite
then arrived anyway (DESIGN.md §6): a body replacement landing on a
proposed task whose last round failed says that round did apply
something, however late, so the recorded outcome is superseded by
applied and the row’s marker flips from ⚠ refine failed to ↻ refined. A rewritten body sitting under a failure marker is worse than
no marker at all: it teaches the operator to disbelieve the one signal
that exists to say a rewrite they asked for silently never happened.
This corrects a concluded round and nothing else — the task neither
re-enters refining nor transitions, and the round’s session keeps the
outcome the reconciler observed of its process. Returns whether anything
was corrected, so a caller can say so; a no-op on any other state or any
other last outcome, and idempotent, since the correction it appends is
itself the newest outcome.
Sourcepub fn latest_refine_note(&self, task_id: i64) -> Result<Option<String>>
pub fn latest_refine_note(&self, task_id: i64) -> Result<Option<String>>
The newest refine note recorded on a task — the note that rode the
proposed → refining transition — for the seed context a refine agent is
launched with and for the detail views.
Sourcepub fn discovered_from(&self, task_id: i64) -> Result<Option<Task>>
pub fn discovered_from(&self, task_id: i64) -> Result<Option<Task>>
The task a proposal was discovered from (the discovered-from edge of
§5), if any — the context a sloppy proposal is usually missing, which is
what a refine session is seeded with. The newest edge wins if a task
somehow carries several.
pub fn add_dep( &mut self, task_id: i64, depends_on: i64, kind: DepKind, ) -> Result<()>
Sourcepub fn remove_dep(
&mut self,
task_id: i64,
depends_on: i64,
kind: DepKind,
) -> Result<()>
pub fn remove_dep( &mut self, task_id: i64, depends_on: i64, kind: DepKind, ) -> Result<()>
Drop one edge. The kind is part of the identity of an edge — a pair may
carry several — so removing a blocker must not take the
discovered-from edge beside it with it.
Sourcepub fn deps_by_task(&self) -> Result<HashMap<i64, Vec<DepRef>>>
pub fn deps_by_task(&self) -> Result<HashMap<i64, Vec<DepRef>>>
Every dependency edge of every kind, keyed by the depending task and
resolved to the dependency’s current title and state — the forward
direction a detail view renders as blocked by #N. One query feeds every
pane, so the render path never issues a per-row lookup.
Sourcepub fn dependents_by_task(&self) -> Result<HashMap<i64, Vec<DepRef>>>
pub fn dependents_by_task(&self) -> Result<HashMap<i64, Vec<DepRef>>>
The reverse edges: every dependency keyed by the task depended on, resolved to the depending task — who a task blocks (or spawned).
pub fn deps_of(&self, task_id: i64) -> Result<Vec<Dep>>
Sourcepub fn create_session(
&mut self,
task_id: i64,
agent: &str,
pid: Option<i64>,
liveness_source: LivenessSource,
log_path: Option<&str>,
) -> Result<Session>
pub fn create_session( &mut self, task_id: i64, agent: &str, pid: Option<i64>, liveness_source: LivenessSource, log_path: Option<&str>, ) -> Result<Session>
Open a session for a running task, stamping started_at. ended_at and
outcome stay NULL until end_session.
liveness_source is which source reconciliation must read the session by
(DESIGN.md §8), which only the caller that spawned the process knows.
Sourcepub fn set_session_ref(&mut self, id: i64, session_ref: &str) -> Result<Session>
pub fn set_session_ref(&mut self, id: i64, session_ref: &str) -> Result<Session>
Record the agent’s own reference for a session, captured
after launch — the row necessarily exists before the reference does,
so this is an update rather than a create_session parameter.
Sourcepub fn record_session_send(
&mut self,
id: i64,
session_ref: Option<&str>,
pid: i64,
) -> Result<Session>
pub fn record_session_send( &mut self, id: i64, session_ref: Option<&str>, pid: i64, ) -> Result<Session>
Record what a confirmed headless send did to a session (DESIGN.md §8): the process now carrying the turn, and — where the agent forked rather than resumed in place — the reference the conversation continues under. One statement, so a reconcile in another window never reads the new reference beside the old process or the reverse.
Sourcepub fn end_session(
&mut self,
id: i64,
outcome: SessionOutcome,
) -> Result<Session>
pub fn end_session( &mut self, id: i64, outcome: SessionOutcome, ) -> Result<Session>
Close a session with its outcome, stamping ended_at.
pub fn session(&self, id: i64) -> Result<Session>
pub fn sessions_for(&self, task_id: i64) -> Result<Vec<Session>>
Sourcepub fn latest_sessions(&self) -> Result<HashMap<i64, Session>>
pub fn latest_sessions(&self) -> Result<HashMap<i64, Session>>
Every task’s newest session, keyed by task id, in one query — what the
TUI loads per refresh to answer “what is/was this session doing?” without
querying the store mid-draw. Session ids are monotonic, so max(id) is
the latest.
Sourcepub fn live_sessions(&self) -> Result<Vec<Session>>
pub fn live_sessions(&self) -> Result<Vec<Session>>
Sessions that have not yet ended, newest first.
Sourcepub fn incomplete_report_flag(&self, task_id: i64) -> Result<bool>
pub fn incomplete_report_flag(&self, task_id: i64) -> Result<bool>
Whether task_id is a review task carrying a half-written completion
report — a branch with no summary (DESIGN.md §8). A summary with no
branch is not flagged: an investigation, triage or audit produces no code
and its summary is the whole deliverable. Gated on review, derived
fresh rather than stored.
Sourcepub fn running_rows(&self) -> Result<Vec<RunningRow>>
pub fn running_rows(&self) -> Result<Vec<RunningRow>>
Rows for the cockpit’s running strip (DESIGN.md §9): every running,
refining, or waiting task, joined with its open session if it has
one. The strip filters on task state, so review/needs-input tasks
(session still open) do not appear, while a refine in flight and a
handed-off task both do — an open session does not imply executing
the task (§8), and what the strip shows is work in flight that someone
else owns. A hand-started task with no session shows with session_id/
agent NULL. The one-open-session invariant (§8) bounds the join to one
row per task; elapsed is computed in SQL so the TUI only formats it.
A waiting task measures its elapsed from state_since rather than its
session: the session opened when the agent started the work, long before
the hand-off, and what the operator wants from a handed-off row is how
long it has been waiting. Waiting rows sort after the rest, since they
are the ones nobody is actively typing into.
Archived projects leave the cockpit entirely (§5), the strip included.
Sourcepub fn latest_summary(&self, task_id: i64) -> Result<Option<String>>
pub fn latest_summary(&self, task_id: i64) -> Result<Option<String>>
The most recent completion summary a task recorded (DESIGN.md §8): the
detail of its newest summary event, logged by done --summary or
amended by set --summary (set_summary). This is the PR body when
pr opens a pull request. None when the task never carried a summary.
Sourcepub fn record_reviewed(&mut self, id: i64, sha: &str) -> Result<()>
pub fn record_reviewed(&mut self, id: i64, sha: &str) -> Result<()>
Record the branch revision the operator has just reviewed (DESIGN.md
§8), so the next look at this task can be narrowed to what the rework
added. Written at rejection, when the head is exactly what was judged.
The events table carries it, so delta re-review costs no column and no
migration; a later recording supersedes an earlier one the way a summary
does.
Sourcepub fn last_reviewed(&self, task_id: i64) -> Result<Option<String>>
pub fn last_reviewed(&self, task_id: i64) -> Result<Option<String>>
The revision recorded by the newest record_reviewed, or None for a
task nobody has reviewed and sent back — which is what keeps a first
review showing the whole diff.
pub fn events_for(&self, task_id: i64) -> Result<Vec<Event>>
Source§impl Store
impl Store
Sourcepub fn legal_actions(state: TaskState, human: bool) -> Vec<Action>
pub fn legal_actions(state: TaskState, human: bool) -> Vec<Action>
Legal target of action from state, if any. Exposed so interfaces can
offer exactly the legal actions without duplicating the machine. Order
matters: interfaces render the first entry selected, so the most common
action leads (for triage, ready). human shortens the running path
(DESIGN.md §6): no ask, and completion leads since it goes straight to
done.
pub fn apply(&mut self, task_id: i64, action: Action) -> Result<Task>
Sourcepub fn apply_closing(
&mut self,
task_id: i64,
action: Action,
) -> Result<(Task, Option<Session>)>
pub fn apply_closing( &mut self, task_id: i64, action: Action, ) -> Result<(Task, Option<Session>)>
apply, plus the session this transition retired, for a
caller that owns process I/O (DESIGN.md §8). A session’s registry entry
follows its row: on the operator’s closing verdicts
(Action::stops_session) the shell fires the agent’s stop verb at
what comes back here, so the agent’s own listing converges on work still
in flight.
None covers every case there is nothing to stop: a transition that
leaves the session open on purpose, one that closes it as the agent’s own
report rather than a verdict on it, and a task that had no open session
at all. The session is read back after the commit, so what comes back is
the closed row rather than the live one it was.
Sourcepub fn record_dispatch(
&mut self,
task_id: i64,
agent: &str,
pid: Option<i64>,
liveness_source: LivenessSource,
log_path: Option<&str>,
) -> Result<(Task, Session)>
pub fn record_dispatch( &mut self, task_id: i64, agent: &str, pid: Option<i64>, liveness_source: LivenessSource, log_path: Option<&str>, ) -> Result<(Task, Session)>
Dispatch’s atomic write (DESIGN.md §8): move the task ready → running
(or stalled → running) and open its session in one transaction, so a
running task always has a session and a session always has a running
task. Spawning the process is the caller’s job, before this commits, and
so is naming its liveness_source (DESIGN.md §8): only the code that
launched knows whether the pid it holds is the work or a launcher.
Sourcepub fn record_refine_launch(
&mut self,
task_id: i64,
note: &str,
agent: &str,
pid: Option<i64>,
liveness_source: LivenessSource,
log_path: Option<&str>,
) -> Result<(Task, Session)>
pub fn record_refine_launch( &mut self, task_id: i64, note: &str, agent: &str, pid: Option<i64>, liveness_source: LivenessSource, log_path: Option<&str>, ) -> Result<(Task, Session)>
Refine’s atomic write (DESIGN.md §6), the shape record_dispatch
established: move the task proposed | ready → refining and open the
round’s session in one transaction, so a refining task always has a
session to probe and a refine session always names a task that is
refining. A round launched from ready still concludes to proposed,
which is what sends the rewritten body back through triage. The
note rides the transition; the empty string is the interactive flavour,
which is a conversation rather than a brief. Spawning the process is the
caller’s job, before this commits, and the flavour it spawned rides in as
liveness_source: the headless round inherits the dispatch template’s
source, the interactive one is the foreground child’s own pid.
Sourcepub fn conclude_refine(
&mut self,
task_id: i64,
outcome: RefineOutcome,
) -> Result<Task>
pub fn conclude_refine( &mut self, task_id: i64, outcome: RefineOutcome, ) -> Result<Task>
End a refine round (DESIGN.md §6): refining → proposed, logging how it
ended and closing the round’s session with the matching outcome. Every
trigger comes through here — the agent’s own set --body-file
(Applied), a reconciled dead agent (Failed), a quit or cancelled
session (Cancelled) — so the returned proposal’s markers read from one
place. The landing is proposed whatever the round started from: the
round keeps no memory of its origin, so a task refined out of ready
comes back for a fresh verdict on the body that replaced the one the old
verdict was issued against. Refused on a task that is not refining, like
any other transition.
Sourcepub fn reconcile_session(
&mut self,
session_id: i64,
pid_alive: bool,
likely_capped: bool,
) -> Result<Option<(Session, Task)>>
pub fn reconcile_session( &mut self, session_id: i64, pid_alive: bool, likely_capped: bool, ) -> Result<Option<(Session, Task)>>
Reconcile an open session against its task’s state (DESIGN.md §8). The
session’s life follows the task, not the process listing, so the terminal
transitions close healthy sessions; reconciliation only catches a crash
or cap mid-running and finalises sessions stranded on already-closed
tasks. pid_alive/likely_capped are supplied by the caller (voro-core
does no process or log I/O) and matter only for a running task:
- session already ended: no-op (
Ok(None)), so a repeated sweep can’t double-finalise it. running,pid_alive: left untouched.running, process gone: outcome recorded (capped/failed) and the task goesrunning → stalled(DESIGN.md §6/§8) — an attention row never handed out byvoro next; a latedonelands it inreviewon the dead session’s behalf. A stalled task with an open blocker demotes toparked.refining, process gone: the agent died without rewriting anything, so the round concludesfailedand the task goes back toproposedcarrying the failed-round marker (DESIGN.md §6).needs-input/review/waiting: the session stays open on purpose (the operator answers in it, or a reject returns the work to it), so this leaves it alone (Ok(None)) regardless of liveness.- task already closed or off the active path: the session is stale, so it
is finalised now (
completedfordone, elseaborted) with no event.
Sourcepub fn set_blocks_deps(
&mut self,
task_id: i64,
depends_on: &[i64],
) -> Result<Task>
pub fn set_blocks_deps( &mut self, task_id: i64, depends_on: &[i64], ) -> Result<Task>
Replace the blocks dependencies of a task with the given set, then
reconcile its readiness. This is the dep-editing entry point for
interfaces; add_dep/remove_dep reconcile too. A repeated id in
depends_on names the same single edge, so the set is deduplicated
rather than left to collide on insert.
Sourcepub fn block_tasks(
&mut self,
blocker_id: i64,
dependents: &[i64],
) -> Result<Vec<(Task, TaskState)>>
pub fn block_tasks( &mut self, blocker_id: i64, dependents: &[i64], ) -> Result<Vec<(Task, TaskState)>>
The reverse authoring direction of
set_blocks_deps: make blocker_id a blocker
of each task in dependents. Additive and idempotent (replacing the set
here would detach edges other tasks authored) — the conflict clause is
scoped to the identical edge, so an edge of another kind between the same
pair coexists with the blocker rather than swallowing it. Each dependent’s
readiness is reconciled in the same write; the returned pairs carry its
prior state so callers can surface a demotion.