swarm repositories / source
aboutsummaryrefslogtreecommitdiff
path: root/crates/fidget-spinner-store-sqlite
diff options
context:
space:
mode:
Diffstat (limited to 'crates/fidget-spinner-store-sqlite')
-rw-r--r--crates/fidget-spinner-store-sqlite/src/lib.rs545
1 files changed, 84 insertions, 461 deletions
diff --git a/crates/fidget-spinner-store-sqlite/src/lib.rs b/crates/fidget-spinner-store-sqlite/src/lib.rs
index 1862590..bbe7038 100644
--- a/crates/fidget-spinner-store-sqlite/src/lib.rs
+++ b/crates/fidget-spinner-store-sqlite/src/lib.rs
@@ -3,18 +3,16 @@ use std::collections::{BTreeMap, BTreeSet};
use std::fmt::Write as _;
use std::fs;
use std::io;
-use std::process::Command;
use camino::{Utf8Path, Utf8PathBuf};
use fidget_spinner_core::{
- AnnotationVisibility, CheckpointDisposition, CheckpointRecord, CheckpointSnapshotRef,
- CodeSnapshotRef, CommandRecipe, CompletedExperiment, DagEdge, DagNode, DiagnosticSeverity,
+ AnnotationVisibility, CommandRecipe, CompletedExperiment, DagEdge, DagNode, DiagnosticSeverity,
EdgeKind, ExecutionBackend, ExperimentResult, FieldPresence, FieldRole, FieldValueType,
FrontierContract, FrontierNote, FrontierProjection, FrontierRecord, FrontierStatus,
- FrontierVerdict, GitCommitHash, InferencePolicy, JsonObject, MetricDefinition, MetricSpec,
- MetricUnit, MetricValue, NodeAnnotation, NodeClass, NodeDiagnostics, NodePayload, NonEmptyText,
- OpenExperiment, OptimizationObjective, ProjectFieldSpec, ProjectSchema, RunDimensionDefinition,
- RunDimensionValue, RunRecord, RunStatus, TagName, TagRecord,
+ FrontierVerdict, FrontierVerdictCounts, InferencePolicy, JsonObject, MetricDefinition,
+ MetricSpec, MetricUnit, MetricValue, NodeAnnotation, NodeClass, NodeDiagnostics, NodePayload,
+ NonEmptyText, OpenExperiment, OptimizationObjective, ProjectFieldSpec, ProjectSchema,
+ RunDimensionDefinition, RunDimensionValue, RunRecord, RunStatus, TagName, TagRecord,
};
use rusqlite::types::Value as SqlValue;
use rusqlite::{Connection, OptionalExtension, Transaction, params, params_from_iter};
@@ -29,7 +27,7 @@ pub const STORE_DIR_NAME: &str = ".fidget_spinner";
pub const STATE_DB_NAME: &str = "state.sqlite";
pub const PROJECT_CONFIG_NAME: &str = "project.json";
pub const PROJECT_SCHEMA_NAME: &str = "schema.json";
-pub const CURRENT_STORE_FORMAT_VERSION: u32 = 2;
+pub const CURRENT_STORE_FORMAT_VERSION: u32 = 3;
#[derive(Debug, Error)]
pub enum StoreError {
@@ -58,8 +56,6 @@ pub enum StoreError {
NodeNotFound(fidget_spinner_core::NodeId),
#[error("frontier {0} was not found")]
FrontierNotFound(fidget_spinner_core::FrontierId),
- #[error("checkpoint {0} was not found")]
- CheckpointNotFound(fidget_spinner_core::CheckpointId),
#[error("experiment {0} was not found")]
ExperimentNotFound(fidget_spinner_core::ExperimentId),
#[error("node {0} is not a hypothesis node")]
@@ -68,10 +64,6 @@ pub enum StoreError {
"project store format {observed} is incompatible with this binary (expected {expected}); reinitialize the store"
)]
IncompatibleStoreFormatVersion { observed: u32, expected: u32 },
- #[error("frontier {frontier_id} has no champion checkpoint")]
- MissingChampionCheckpoint {
- frontier_id: fidget_spinner_core::FrontierId,
- },
#[error("unknown tag `{0}`")]
UnknownTag(TagName),
#[error("tag `{0}` already exists")]
@@ -82,8 +74,6 @@ pub enum StoreError {
ProseSummaryRequired(NodeClass),
#[error("{0} nodes require a non-empty string payload field `body`")]
ProseBodyRequired(NodeClass),
- #[error("git repository inspection failed for {0}")]
- GitInspectionFailed(Utf8PathBuf),
#[error("metric `{0}` is not registered")]
UnknownMetricDefinition(NonEmptyText),
#[error(
@@ -301,13 +291,12 @@ pub struct MetricBestEntry {
pub value: f64,
pub order: MetricRankOrder,
pub experiment_id: fidget_spinner_core::ExperimentId,
+ pub experiment_title: NonEmptyText,
pub frontier_id: fidget_spinner_core::FrontierId,
pub hypothesis_node_id: fidget_spinner_core::NodeId,
pub hypothesis_title: NonEmptyText,
pub run_id: fidget_spinner_core::RunId,
pub verdict: FrontierVerdict,
- pub candidate_checkpoint_id: fidget_spinner_core::CheckpointId,
- pub candidate_commit_hash: GitCommitHash,
pub unit: Option<MetricUnit>,
pub objective: Option<OptimizationObjective>,
pub dimensions: BTreeMap<NonEmptyText, RunDimensionValue>,
@@ -375,19 +364,11 @@ pub struct CreateFrontierRequest {
pub contract_title: NonEmptyText,
pub contract_summary: Option<NonEmptyText>,
pub contract: FrontierContract,
- pub initial_checkpoint: Option<CheckpointSeed>,
-}
-
-#[derive(Clone, Debug)]
-pub struct CheckpointSeed {
- pub summary: NonEmptyText,
- pub snapshot: CheckpointSnapshotRef,
}
#[derive(Clone, Debug)]
pub struct OpenExperimentRequest {
pub frontier_id: fidget_spinner_core::FrontierId,
- pub base_checkpoint_id: fidget_spinner_core::CheckpointId,
pub hypothesis_node_id: fidget_spinner_core::NodeId,
pub title: NonEmptyText,
pub summary: Option<NonEmptyText>,
@@ -397,7 +378,6 @@ pub struct OpenExperimentRequest {
pub struct OpenExperimentSummary {
pub id: fidget_spinner_core::ExperimentId,
pub frontier_id: fidget_spinner_core::FrontierId,
- pub base_checkpoint_id: fidget_spinner_core::CheckpointId,
pub hypothesis_node_id: fidget_spinner_core::NodeId,
pub title: NonEmptyText,
pub summary: Option<NonEmptyText>,
@@ -414,14 +394,11 @@ pub struct ExperimentAnalysisDraft {
#[derive(Clone, Debug)]
pub struct CloseExperimentRequest {
pub experiment_id: fidget_spinner_core::ExperimentId,
- pub candidate_summary: NonEmptyText,
- pub candidate_snapshot: CheckpointSnapshotRef,
pub run_title: NonEmptyText,
pub run_summary: Option<NonEmptyText>,
pub backend: ExecutionBackend,
pub dimensions: BTreeMap<NonEmptyText, RunDimensionValue>,
pub command: CommandRecipe,
- pub code_snapshot: Option<CodeSnapshotRef>,
pub primary_metric: MetricValue,
pub supporting_metrics: Vec<MetricValue>,
pub note: FrontierNote,
@@ -434,7 +411,6 @@ pub struct CloseExperimentRequest {
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct ExperimentReceipt {
pub open_experiment: OpenExperiment,
- pub checkpoint: CheckpointRecord,
pub run_node: DagNode,
pub run: RunRecord,
pub analysis_node: Option<DagNode>,
@@ -629,18 +605,6 @@ impl ProjectStore {
}
insert_node(&tx, &contract_node)?;
insert_frontier(&tx, &frontier)?;
- if let Some(seed) = request.initial_checkpoint {
- let checkpoint = CheckpointRecord {
- id: fidget_spinner_core::CheckpointId::fresh(),
- frontier_id: frontier.id,
- node_id: contract_node.id,
- snapshot: seed.snapshot,
- disposition: CheckpointDisposition::Champion,
- summary: seed.summary,
- created_at: OffsetDateTime::now_utc(),
- };
- insert_checkpoint(&tx, &checkpoint)?;
- }
insert_event(
&tx,
"frontier",
@@ -1074,68 +1038,43 @@ impl ProjectStore {
frontier_id: fidget_spinner_core::FrontierId,
) -> Result<FrontierProjection, StoreError> {
let frontier = self.load_frontier(frontier_id)?;
- let mut champion_checkpoint_id = None;
- let mut candidate_checkpoint_ids = BTreeSet::new();
-
- let mut statement = self.connection.prepare(
- "SELECT id, disposition
- FROM checkpoints
- WHERE frontier_id = ?1",
- )?;
- let mut rows = statement.query(params![frontier_id.to_string()])?;
- while let Some(row) = rows.next()? {
- let checkpoint_id = parse_checkpoint_id(&row.get::<_, String>(0)?)?;
- match parse_checkpoint_disposition(&row.get::<_, String>(1)?)? {
- CheckpointDisposition::Champion => champion_checkpoint_id = Some(checkpoint_id),
- CheckpointDisposition::FrontierCandidate => {
- let _ = candidate_checkpoint_ids.insert(checkpoint_id);
- }
- CheckpointDisposition::Baseline
- | CheckpointDisposition::DeadEnd
- | CheckpointDisposition::Archived => {}
- }
- }
- let experiment_count = self.connection.query_row(
+ let open_experiment_count = self.connection.query_row(
+ "SELECT COUNT(*) FROM open_experiments WHERE frontier_id = ?1",
+ params![frontier_id.to_string()],
+ |row| row.get::<_, i64>(0),
+ )? as u64;
+ let completed_experiment_count = self.connection.query_row(
"SELECT COUNT(*) FROM experiments WHERE frontier_id = ?1",
params![frontier_id.to_string()],
|row| row.get::<_, i64>(0),
)? as u64;
+ let verdict_counts = self.connection.query_row(
+ "SELECT
+ SUM(CASE WHEN verdict = 'accepted' THEN 1 ELSE 0 END),
+ SUM(CASE WHEN verdict = 'kept' THEN 1 ELSE 0 END),
+ SUM(CASE WHEN verdict = 'parked' THEN 1 ELSE 0 END),
+ SUM(CASE WHEN verdict = 'rejected' THEN 1 ELSE 0 END)
+ FROM experiments
+ WHERE frontier_id = ?1",
+ params![frontier_id.to_string()],
+ |row| {
+ Ok(FrontierVerdictCounts {
+ accepted: row.get::<_, Option<i64>>(0)?.unwrap_or(0) as u64,
+ kept: row.get::<_, Option<i64>>(1)?.unwrap_or(0) as u64,
+ parked: row.get::<_, Option<i64>>(2)?.unwrap_or(0) as u64,
+ rejected: row.get::<_, Option<i64>>(3)?.unwrap_or(0) as u64,
+ })
+ },
+ )?;
Ok(FrontierProjection {
frontier,
- champion_checkpoint_id,
- candidate_checkpoint_ids,
- experiment_count,
+ open_experiment_count,
+ completed_experiment_count,
+ verdict_counts,
})
}
- pub fn load_checkpoint(
- &self,
- checkpoint_id: fidget_spinner_core::CheckpointId,
- ) -> Result<Option<CheckpointRecord>, StoreError> {
- let mut statement = self.connection.prepare(
- "SELECT
- id,
- frontier_id,
- node_id,
- repo_root,
- worktree_root,
- worktree_name,
- commit_hash,
- disposition,
- summary,
- created_at
- FROM checkpoints
- WHERE id = ?1",
- )?;
- statement
- .query_row(params![checkpoint_id.to_string()], |row| {
- read_checkpoint_row(row)
- })
- .optional()
- .map_err(StoreError::from)
- }
-
pub fn open_experiment(
&mut self,
request: OpenExperimentRequest,
@@ -1149,16 +1088,9 @@ impl ProjectStore {
if hypothesis_node.frontier_id != Some(request.frontier_id) {
return Err(StoreError::FrontierNotFound(request.frontier_id));
}
- let base_checkpoint = self
- .load_checkpoint(request.base_checkpoint_id)?
- .ok_or(StoreError::CheckpointNotFound(request.base_checkpoint_id))?;
- if base_checkpoint.frontier_id != request.frontier_id {
- return Err(StoreError::CheckpointNotFound(request.base_checkpoint_id));
- }
let experiment = OpenExperiment {
id: fidget_spinner_core::ExperimentId::fresh(),
frontier_id: request.frontier_id,
- base_checkpoint_id: request.base_checkpoint_id,
hypothesis_node_id: request.hypothesis_node_id,
title: request.title,
summary: request.summary,
@@ -1175,7 +1107,6 @@ impl ProjectStore {
json!({
"frontier_id": experiment.frontier_id,
"hypothesis_node_id": experiment.hypothesis_node_id,
- "base_checkpoint_id": experiment.base_checkpoint_id,
}),
)?;
tx.commit()?;
@@ -1190,7 +1121,6 @@ impl ProjectStore {
"SELECT
id,
frontier_id,
- base_checkpoint_id,
hypothesis_node_id,
title,
summary,
@@ -1205,14 +1135,13 @@ impl ProjectStore {
items.push(OpenExperimentSummary {
id: parse_experiment_id(&row.get::<_, String>(0)?)?,
frontier_id: parse_frontier_id(&row.get::<_, String>(1)?)?,
- base_checkpoint_id: parse_checkpoint_id(&row.get::<_, String>(2)?)?,
- hypothesis_node_id: parse_node_id(&row.get::<_, String>(3)?)?,
- title: NonEmptyText::new(row.get::<_, String>(4)?)?,
+ hypothesis_node_id: parse_node_id(&row.get::<_, String>(2)?)?,
+ title: NonEmptyText::new(row.get::<_, String>(3)?)?,
summary: row
- .get::<_, Option<String>>(5)?
+ .get::<_, Option<String>>(4)?
.map(NonEmptyText::new)
.transpose()?,
- created_at: decode_timestamp(&row.get::<_, String>(6)?)?,
+ created_at: decode_timestamp(&row.get::<_, String>(5)?)?,
});
}
Ok(items)
@@ -1241,16 +1170,6 @@ impl ProjectStore {
open_experiment.hypothesis_node_id,
));
}
- let base_checkpoint = self
- .load_checkpoint(open_experiment.base_checkpoint_id)?
- .ok_or(StoreError::CheckpointNotFound(
- open_experiment.base_checkpoint_id,
- ))?;
- if base_checkpoint.frontier_id != open_experiment.frontier_id {
- return Err(StoreError::CheckpointNotFound(
- open_experiment.base_checkpoint_id,
- ));
- }
let tx = self.connection.transaction()?;
let dimensions = validate_run_dimensions_tx(&tx, &request.dimensions)?;
let primary_metric_definition =
@@ -1292,7 +1211,6 @@ impl ProjectStore {
frontier_id: Some(open_experiment.frontier_id),
status: RunStatus::Succeeded,
backend: request.backend,
- code_snapshot: request.code_snapshot,
dimensions: dimensions.clone(),
command: request.command,
started_at: Some(now),
@@ -1339,28 +1257,9 @@ impl ProjectStore {
decision_diagnostics,
);
- let checkpoint = CheckpointRecord {
- id: fidget_spinner_core::CheckpointId::fresh(),
- frontier_id: open_experiment.frontier_id,
- node_id: run_node.id,
- snapshot: request.candidate_snapshot,
- disposition: match request.verdict {
- FrontierVerdict::PromoteToChampion => CheckpointDisposition::Champion,
- FrontierVerdict::KeepOnFrontier | FrontierVerdict::NeedsMoreEvidence => {
- CheckpointDisposition::FrontierCandidate
- }
- FrontierVerdict::RevertToChampion => CheckpointDisposition::DeadEnd,
- FrontierVerdict::ArchiveDeadEnd => CheckpointDisposition::Archived,
- },
- summary: request.candidate_summary,
- created_at: now,
- };
-
let experiment = CompletedExperiment {
id: open_experiment.id,
frontier_id: open_experiment.frontier_id,
- base_checkpoint_id: open_experiment.base_checkpoint_id,
- candidate_checkpoint_id: checkpoint.id,
hypothesis_node_id: open_experiment.hypothesis_node_id,
run_node_id: run_node.id,
run_id,
@@ -1428,16 +1327,6 @@ impl ProjectStore {
supporting_metric_definitions.as_slice(),
)?;
insert_run_dimensions(&tx, run.run_id, &dimensions)?;
- match request.verdict {
- FrontierVerdict::PromoteToChampion => {
- demote_previous_champion(&tx, open_experiment.frontier_id)?;
- }
- FrontierVerdict::KeepOnFrontier
- | FrontierVerdict::NeedsMoreEvidence
- | FrontierVerdict::RevertToChampion
- | FrontierVerdict::ArchiveDeadEnd => {}
- }
- insert_checkpoint(&tx, &checkpoint)?;
insert_experiment(&tx, &experiment)?;
delete_open_experiment(&tx, open_experiment.id)?;
touch_frontier(&tx, open_experiment.frontier_id)?;
@@ -1450,14 +1339,12 @@ impl ProjectStore {
"frontier_id": open_experiment.frontier_id,
"hypothesis_node_id": open_experiment.hypothesis_node_id,
"verdict": format!("{:?}", request.verdict),
- "candidate_checkpoint_id": checkpoint.id,
}),
)?;
tx.commit()?;
Ok(ExperimentReceipt {
open_experiment,
- checkpoint,
run_node,
run,
analysis_node,
@@ -1466,13 +1353,6 @@ impl ProjectStore {
})
}
- pub fn auto_capture_checkpoint(
- &self,
- summary: NonEmptyText,
- ) -> Result<Option<CheckpointSeed>, StoreError> {
- auto_capture_checkpoint_seed(&self.project_root, summary)
- }
-
fn load_annotations(
&self,
node_id: fidget_spinner_core::NodeId,
@@ -1565,12 +1445,11 @@ struct MetricSample {
value: f64,
frontier_id: fidget_spinner_core::FrontierId,
experiment_id: fidget_spinner_core::ExperimentId,
+ experiment_title: NonEmptyText,
hypothesis_node_id: fidget_spinner_core::NodeId,
hypothesis_title: NonEmptyText,
run_id: fidget_spinner_core::RunId,
verdict: FrontierVerdict,
- candidate_checkpoint_id: fidget_spinner_core::CheckpointId,
- candidate_commit_hash: GitCommitHash,
unit: Option<MetricUnit>,
objective: Option<OptimizationObjective>,
dimensions: BTreeMap<NonEmptyText, RunDimensionValue>,
@@ -1584,13 +1463,12 @@ impl MetricSample {
value: self.value,
order,
experiment_id: self.experiment_id,
+ experiment_title: self.experiment_title,
frontier_id: self.frontier_id,
hypothesis_node_id: self.hypothesis_node_id,
hypothesis_title: self.hypothesis_title,
run_id: self.run_id,
verdict: self.verdict,
- candidate_checkpoint_id: self.candidate_checkpoint_id,
- candidate_commit_hash: self.candidate_commit_hash,
unit: self.unit,
objective: self.objective,
dimensions: self.dimensions,
@@ -1737,10 +1615,10 @@ fn compare_metric_samples(
#[derive(Clone, Debug)]
struct ExperimentMetricRow {
experiment_id: fidget_spinner_core::ExperimentId,
+ experiment_title: NonEmptyText,
frontier_id: fidget_spinner_core::FrontierId,
run_id: fidget_spinner_core::RunId,
verdict: FrontierVerdict,
- candidate_checkpoint: CheckpointRecord,
hypothesis_node: DagNode,
run_node: DagNode,
analysis_node: Option<DagNode>,
@@ -1755,13 +1633,13 @@ fn load_experiment_rows(store: &ProjectStore) -> Result<Vec<ExperimentMetricRow>
let mut statement = store.connection.prepare(
"SELECT
id,
+ title,
frontier_id,
run_id,
hypothesis_node_id,
run_node_id,
analysis_node_id,
decision_node_id,
- candidate_checkpoint_id,
primary_metric_json,
supporting_metrics_json,
verdict
@@ -1770,23 +1648,20 @@ fn load_experiment_rows(store: &ProjectStore) -> Result<Vec<ExperimentMetricRow>
let mut rows = statement.query([])?;
let mut items = Vec::new();
while let Some(row) = rows.next()? {
- let hypothesis_node_id = parse_node_id(&row.get::<_, String>(3)?)?;
- let run_id = parse_run_id(&row.get::<_, String>(2)?)?;
- let run_node_id = parse_node_id(&row.get::<_, String>(4)?)?;
+ let hypothesis_node_id = parse_node_id(&row.get::<_, String>(4)?)?;
+ let run_id = parse_run_id(&row.get::<_, String>(3)?)?;
+ let run_node_id = parse_node_id(&row.get::<_, String>(5)?)?;
let analysis_node_id = row
- .get::<_, Option<String>>(5)?
+ .get::<_, Option<String>>(6)?
.map(|raw| parse_node_id(&raw))
.transpose()?;
- let decision_node_id = parse_node_id(&row.get::<_, String>(6)?)?;
- let candidate_checkpoint_id = parse_checkpoint_id(&row.get::<_, String>(7)?)?;
+ let decision_node_id = parse_node_id(&row.get::<_, String>(7)?)?;
items.push(ExperimentMetricRow {
experiment_id: parse_experiment_id(&row.get::<_, String>(0)?)?,
- frontier_id: parse_frontier_id(&row.get::<_, String>(1)?)?,
+ experiment_title: NonEmptyText::new(row.get::<_, String>(1)?)?,
+ frontier_id: parse_frontier_id(&row.get::<_, String>(2)?)?,
run_id,
verdict: parse_frontier_verdict(&row.get::<_, String>(10)?)?,
- candidate_checkpoint: store
- .load_checkpoint(candidate_checkpoint_id)?
- .ok_or(StoreError::CheckpointNotFound(candidate_checkpoint_id))?,
hypothesis_node: store
.get_node(hypothesis_node_id)?
.ok_or(StoreError::NodeNotFound(hypothesis_node_id))?,
@@ -1856,12 +1731,11 @@ fn metric_sample_from_observation(
value: metric.value,
frontier_id: row.frontier_id,
experiment_id: row.experiment_id,
+ experiment_title: row.experiment_title.clone(),
hypothesis_node_id: row.hypothesis_node.id,
hypothesis_title: row.hypothesis_node.title.clone(),
run_id: row.run_id,
verdict: row.verdict,
- candidate_checkpoint_id: row.candidate_checkpoint.id,
- candidate_commit_hash: row.candidate_checkpoint.snapshot.commit_hash.clone(),
unit: registry.map(|definition| definition.unit),
objective: registry.map(|definition| definition.objective),
dimensions: row.dimensions.clone(),
@@ -1895,12 +1769,11 @@ fn metric_samples_from_payload(
value,
frontier_id: row.frontier_id,
experiment_id: row.experiment_id,
+ experiment_title: row.experiment_title.clone(),
hypothesis_node_id: row.hypothesis_node.id,
hypothesis_title: row.hypothesis_node.title.clone(),
run_id: row.run_id,
verdict: row.verdict,
- candidate_checkpoint_id: row.candidate_checkpoint.id,
- candidate_commit_hash: row.candidate_checkpoint.snapshot.commit_hash.clone(),
unit: None,
objective: None,
dimensions: row.dimensions.clone(),
@@ -1968,30 +1841,12 @@ fn migrate(connection: &Connection) -> Result<(), StoreError> {
updated_at TEXT NOT NULL
);
- CREATE TABLE IF NOT EXISTS checkpoints (
- id TEXT PRIMARY KEY,
- frontier_id TEXT NOT NULL REFERENCES frontiers(id) ON DELETE CASCADE,
- node_id TEXT NOT NULL REFERENCES nodes(id) ON DELETE RESTRICT,
- repo_root TEXT NOT NULL,
- worktree_root TEXT NOT NULL,
- worktree_name TEXT,
- commit_hash TEXT NOT NULL,
- disposition TEXT NOT NULL,
- summary TEXT NOT NULL,
- created_at TEXT NOT NULL
- );
-
CREATE TABLE IF NOT EXISTS runs (
run_id TEXT PRIMARY KEY,
node_id TEXT NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
frontier_id TEXT REFERENCES frontiers(id) ON DELETE SET NULL,
status TEXT NOT NULL,
backend TEXT NOT NULL,
- repo_root TEXT,
- worktree_root TEXT,
- worktree_name TEXT,
- head_commit TEXT,
- dirty_paths_json TEXT,
benchmark_suite TEXT,
working_directory TEXT NOT NULL,
argv_json TEXT NOT NULL,
@@ -2037,7 +1892,6 @@ fn migrate(connection: &Connection) -> Result<(), StoreError> {
CREATE TABLE IF NOT EXISTS open_experiments (
id TEXT PRIMARY KEY,
frontier_id TEXT NOT NULL REFERENCES frontiers(id) ON DELETE CASCADE,
- base_checkpoint_id TEXT NOT NULL REFERENCES checkpoints(id) ON DELETE RESTRICT,
hypothesis_node_id TEXT NOT NULL REFERENCES nodes(id) ON DELETE RESTRICT,
title TEXT NOT NULL,
summary TEXT,
@@ -2047,8 +1901,6 @@ fn migrate(connection: &Connection) -> Result<(), StoreError> {
CREATE TABLE IF NOT EXISTS experiments (
id TEXT PRIMARY KEY,
frontier_id TEXT NOT NULL REFERENCES frontiers(id) ON DELETE CASCADE,
- base_checkpoint_id TEXT NOT NULL REFERENCES checkpoints(id) ON DELETE RESTRICT,
- candidate_checkpoint_id TEXT NOT NULL REFERENCES checkpoints(id) ON DELETE RESTRICT,
hypothesis_node_id TEXT NOT NULL REFERENCES nodes(id) ON DELETE RESTRICT,
run_node_id TEXT NOT NULL REFERENCES nodes(id) ON DELETE RESTRICT,
run_id TEXT NOT NULL REFERENCES runs(run_id) ON DELETE RESTRICT,
@@ -2903,43 +2755,6 @@ fn insert_frontier(tx: &Transaction<'_>, frontier: &FrontierRecord) -> Result<()
Ok(())
}
-fn insert_checkpoint(
- tx: &Transaction<'_>,
- checkpoint: &CheckpointRecord,
-) -> Result<(), StoreError> {
- let _ = tx.execute(
- "INSERT INTO checkpoints (
- id,
- frontier_id,
- node_id,
- repo_root,
- worktree_root,
- worktree_name,
- commit_hash,
- disposition,
- summary,
- created_at
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
- params![
- checkpoint.id.to_string(),
- checkpoint.frontier_id.to_string(),
- checkpoint.node_id.to_string(),
- checkpoint.snapshot.repo_root.as_str(),
- checkpoint.snapshot.worktree_root.as_str(),
- checkpoint
- .snapshot
- .worktree_name
- .as_ref()
- .map(NonEmptyText::as_str),
- checkpoint.snapshot.commit_hash.as_str(),
- encode_checkpoint_disposition(checkpoint.disposition),
- checkpoint.summary.as_str(),
- encode_timestamp(checkpoint.created_at)?,
- ],
- )?;
- Ok(())
-}
-
fn insert_run(
tx: &Transaction<'_>,
run: &RunRecord,
@@ -2949,28 +2764,6 @@ fn insert_run(
supporting_metrics: &[MetricValue],
supporting_metric_definitions: &[MetricDefinition],
) -> Result<(), StoreError> {
- let (repo_root, worktree_root, worktree_name, head_commit, dirty_paths) = run
- .code_snapshot
- .as_ref()
- .map_or((None, None, None, None, None), |snapshot| {
- (
- Some(snapshot.repo_root.as_str().to_owned()),
- Some(snapshot.worktree_root.as_str().to_owned()),
- snapshot.worktree_name.as_ref().map(ToOwned::to_owned),
- snapshot.head_commit.as_ref().map(ToOwned::to_owned),
- Some(
- snapshot
- .dirty_paths
- .iter()
- .map(ToOwned::to_owned)
- .collect::<Vec<_>>(),
- ),
- )
- });
- let dirty_paths_json = match dirty_paths.as_ref() {
- Some(paths) => Some(encode_json(paths)?),
- None => None,
- };
let started_at = match run.started_at {
Some(timestamp) => Some(encode_timestamp(timestamp)?),
None => None,
@@ -2986,29 +2779,19 @@ fn insert_run(
frontier_id,
status,
backend,
- repo_root,
- worktree_root,
- worktree_name,
- head_commit,
- dirty_paths_json,
benchmark_suite,
working_directory,
argv_json,
env_json,
started_at,
finished_at
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)",
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)",
params![
run.run_id.to_string(),
run.node_id.to_string(),
run.frontier_id.map(|id| id.to_string()),
encode_run_status(run.status),
encode_backend(run.backend),
- repo_root,
- worktree_root,
- worktree_name.map(|item| item.to_string()),
- head_commit.map(|item| item.to_string()),
- dirty_paths_json,
benchmark_suite,
run.command.working_directory.as_str(),
encode_json(&run.command.argv)?,
@@ -3046,16 +2829,14 @@ fn insert_open_experiment(
"INSERT INTO open_experiments (
id,
frontier_id,
- base_checkpoint_id,
hypothesis_node_id,
title,
summary,
created_at
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
params![
experiment.id.to_string(),
experiment.frontier_id.to_string(),
- experiment.base_checkpoint_id.to_string(),
experiment.hypothesis_node_id.to_string(),
experiment.title.as_str(),
experiment.summary.as_ref().map(NonEmptyText::as_str),
@@ -3084,8 +2865,6 @@ fn insert_experiment(
"INSERT INTO experiments (
id,
frontier_id,
- base_checkpoint_id,
- candidate_checkpoint_id,
hypothesis_node_id,
run_node_id,
run_id,
@@ -3100,12 +2879,10 @@ fn insert_experiment(
note_next_json,
verdict,
created_at
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)",
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16)",
params![
experiment.id.to_string(),
experiment.frontier_id.to_string(),
- experiment.base_checkpoint_id.to_string(),
- experiment.candidate_checkpoint_id.to_string(),
experiment.hypothesis_node_id.to_string(),
experiment.run_node_id.to_string(),
experiment.run_id.to_string(),
@@ -3154,7 +2931,6 @@ fn load_open_experiment(
"SELECT
id,
frontier_id,
- base_checkpoint_id,
hypothesis_node_id,
title,
summary,
@@ -3169,18 +2945,16 @@ fn load_open_experiment(
.map_err(to_sql_conversion_error)?,
frontier_id: parse_frontier_id(&row.get::<_, String>(1)?)
.map_err(to_sql_conversion_error)?,
- base_checkpoint_id: parse_checkpoint_id(&row.get::<_, String>(2)?)
- .map_err(to_sql_conversion_error)?,
- hypothesis_node_id: parse_node_id(&row.get::<_, String>(3)?)
+ hypothesis_node_id: parse_node_id(&row.get::<_, String>(2)?)
.map_err(to_sql_conversion_error)?,
- title: NonEmptyText::new(row.get::<_, String>(4)?)
+ title: NonEmptyText::new(row.get::<_, String>(3)?)
.map_err(core_to_sql_conversion_error)?,
summary: row
- .get::<_, Option<String>>(5)?
+ .get::<_, Option<String>>(4)?
.map(NonEmptyText::new)
.transpose()
.map_err(core_to_sql_conversion_error)?,
- created_at: decode_timestamp(&row.get::<_, String>(6)?)
+ created_at: decode_timestamp(&row.get::<_, String>(5)?)
.map_err(to_sql_conversion_error)?,
})
})
@@ -3192,7 +2966,6 @@ fn summarize_open_experiment(experiment: &OpenExperiment) -> OpenExperimentSumma
OpenExperimentSummary {
id: experiment.id,
frontier_id: experiment.frontier_id,
- base_checkpoint_id: experiment.base_checkpoint_id,
hypothesis_node_id: experiment.hypothesis_node_id,
title: experiment.title.clone(),
summary: experiment.summary.clone(),
@@ -3214,19 +2987,6 @@ fn touch_frontier(
Ok(())
}
-fn demote_previous_champion(
- tx: &Transaction<'_>,
- frontier_id: fidget_spinner_core::FrontierId,
-) -> Result<(), StoreError> {
- let _ = tx.execute(
- "UPDATE checkpoints
- SET disposition = 'baseline'
- WHERE frontier_id = ?1 AND disposition = 'champion'",
- params![frontier_id.to_string()],
- )?;
- Ok(())
-}
-
fn read_node_row(row: &rusqlite::Row<'_>) -> Result<DagNode, rusqlite::Error> {
let payload_json = row.get::<_, String>(9)?;
let diagnostics_json = row.get::<_, String>(10)?;
@@ -3276,31 +3036,6 @@ fn read_frontier_row(row: &rusqlite::Row<'_>) -> Result<FrontierRecord, StoreErr
})
}
-fn read_checkpoint_row(row: &rusqlite::Row<'_>) -> Result<CheckpointRecord, rusqlite::Error> {
- Ok(CheckpointRecord {
- id: parse_checkpoint_id(&row.get::<_, String>(0)?).map_err(to_sql_conversion_error)?,
- frontier_id: parse_frontier_id(&row.get::<_, String>(1)?)
- .map_err(to_sql_conversion_error)?,
- node_id: parse_node_id(&row.get::<_, String>(2)?).map_err(to_sql_conversion_error)?,
- snapshot: CheckpointSnapshotRef {
- repo_root: Utf8PathBuf::from(row.get::<_, String>(3)?),
- worktree_root: Utf8PathBuf::from(row.get::<_, String>(4)?),
- worktree_name: row
- .get::<_, Option<String>>(5)?
- .map(NonEmptyText::new)
- .transpose()
- .map_err(core_to_sql_conversion_error)?,
- commit_hash: GitCommitHash::new(row.get::<_, String>(6)?)
- .map_err(core_to_sql_conversion_error)?,
- },
- disposition: parse_checkpoint_disposition(&row.get::<_, String>(7)?)
- .map_err(to_sql_conversion_error)?,
- summary: NonEmptyText::new(row.get::<_, String>(8)?)
- .map_err(core_to_sql_conversion_error)?,
- created_at: decode_timestamp(&row.get::<_, String>(9)?).map_err(to_sql_conversion_error)?,
- })
-}
-
fn frontier_contract_payload(contract: &FrontierContract) -> Result<JsonObject, StoreError> {
json_object(json!({
"objective": contract.objective.as_str(),
@@ -3395,44 +3130,6 @@ fn discovery_start(path: &Utf8Path) -> Utf8PathBuf {
}
}
-fn auto_capture_checkpoint_seed(
- project_root: &Utf8Path,
- summary: NonEmptyText,
-) -> Result<Option<CheckpointSeed>, StoreError> {
- let top_level = git_output(project_root, &["rev-parse", "--show-toplevel"])?;
- let Some(repo_root) = top_level else {
- return Ok(None);
- };
- let commit_hash = git_output(project_root, &["rev-parse", "HEAD"])?
- .ok_or_else(|| StoreError::GitInspectionFailed(project_root.to_path_buf()))?;
- let worktree_name = git_output(project_root, &["rev-parse", "--abbrev-ref", "HEAD"])?;
- Ok(Some(CheckpointSeed {
- summary,
- snapshot: CheckpointSnapshotRef {
- repo_root: Utf8PathBuf::from(repo_root),
- worktree_root: project_root.to_path_buf(),
- worktree_name: worktree_name.map(NonEmptyText::new).transpose()?,
- commit_hash: GitCommitHash::new(commit_hash)?,
- },
- }))
-}
-
-fn git_output(project_root: &Utf8Path, args: &[&str]) -> Result<Option<String>, StoreError> {
- let output = Command::new("git")
- .arg("-C")
- .arg(project_root.as_str())
- .args(args)
- .output()?;
- if !output.status.success() {
- return Ok(None);
- }
- let text = String::from_utf8_lossy(&output.stdout).trim().to_owned();
- if text.is_empty() {
- return Ok(None);
- }
- Ok(Some(text))
-}
-
fn to_sql_conversion_error(error: StoreError) -> rusqlite::Error {
rusqlite::Error::FromSqlConversionFailure(0, rusqlite::types::Type::Text, Box::new(error))
}
@@ -3453,12 +3150,6 @@ fn parse_frontier_id(raw: &str) -> Result<fidget_spinner_core::FrontierId, Store
Ok(fidget_spinner_core::FrontierId::from_uuid(parse_uuid(raw)?))
}
-fn parse_checkpoint_id(raw: &str) -> Result<fidget_spinner_core::CheckpointId, StoreError> {
- Ok(fidget_spinner_core::CheckpointId::from_uuid(parse_uuid(
- raw,
- )?))
-}
-
fn parse_experiment_id(raw: &str) -> Result<fidget_spinner_core::ExperimentId, StoreError> {
Ok(fidget_spinner_core::ExperimentId::from_uuid(parse_uuid(
raw,
@@ -3565,30 +3256,6 @@ fn parse_frontier_status(raw: &str) -> Result<FrontierStatus, StoreError> {
}
}
-fn encode_checkpoint_disposition(disposition: CheckpointDisposition) -> &'static str {
- match disposition {
- CheckpointDisposition::Champion => "champion",
- CheckpointDisposition::FrontierCandidate => "frontier-candidate",
- CheckpointDisposition::Baseline => "baseline",
- CheckpointDisposition::DeadEnd => "dead-end",
- CheckpointDisposition::Archived => "archived",
- }
-}
-
-fn parse_checkpoint_disposition(raw: &str) -> Result<CheckpointDisposition, StoreError> {
- match raw {
- "champion" => Ok(CheckpointDisposition::Champion),
- "frontier-candidate" => Ok(CheckpointDisposition::FrontierCandidate),
- "baseline" => Ok(CheckpointDisposition::Baseline),
- "dead-end" => Ok(CheckpointDisposition::DeadEnd),
- "archived" => Ok(CheckpointDisposition::Archived),
- other => Err(StoreError::Json(serde_json::Error::io(io::Error::new(
- io::ErrorKind::InvalidData,
- format!("unknown checkpoint disposition `{other}`"),
- )))),
- }
-}
-
fn encode_run_status(status: RunStatus) -> &'static str {
match status {
RunStatus::Queued => "queued",
@@ -3670,21 +3337,19 @@ fn decode_optimization_objective(raw: &str) -> Result<OptimizationObjective, Sto
fn encode_frontier_verdict(verdict: FrontierVerdict) -> &'static str {
match verdict {
- FrontierVerdict::PromoteToChampion => "promote-to-champion",
- FrontierVerdict::KeepOnFrontier => "keep-on-frontier",
- FrontierVerdict::RevertToChampion => "revert-to-champion",
- FrontierVerdict::ArchiveDeadEnd => "archive-dead-end",
- FrontierVerdict::NeedsMoreEvidence => "needs-more-evidence",
+ FrontierVerdict::Accepted => "accepted",
+ FrontierVerdict::Kept => "kept",
+ FrontierVerdict::Parked => "parked",
+ FrontierVerdict::Rejected => "rejected",
}
}
fn parse_frontier_verdict(raw: &str) -> Result<FrontierVerdict, StoreError> {
match raw {
- "promote-to-champion" => Ok(FrontierVerdict::PromoteToChampion),
- "keep-on-frontier" => Ok(FrontierVerdict::KeepOnFrontier),
- "revert-to-champion" => Ok(FrontierVerdict::RevertToChampion),
- "archive-dead-end" => Ok(FrontierVerdict::ArchiveDeadEnd),
- "needs-more-evidence" => Ok(FrontierVerdict::NeedsMoreEvidence),
+ "accepted" => Ok(FrontierVerdict::Accepted),
+ "kept" => Ok(FrontierVerdict::Kept),
+ "parked" => Ok(FrontierVerdict::Parked),
+ "rejected" => Ok(FrontierVerdict::Rejected),
other => Err(StoreError::Json(serde_json::Error::io(io::Error::new(
io::ErrorKind::InvalidData,
format!("unknown frontier verdict `{other}`"),
@@ -3785,10 +3450,10 @@ mod tests {
RemoveSchemaFieldRequest, UpsertSchemaFieldRequest,
};
use fidget_spinner_core::{
- CheckpointSnapshotRef, CommandRecipe, DiagnosticSeverity, EvaluationProtocol,
- FieldPresence, FieldRole, FieldValueType, FrontierContract, FrontierNote, FrontierVerdict,
- GitCommitHash, InferencePolicy, MetricSpec, MetricUnit, MetricValue, NodeAnnotation,
- NodeClass, NodePayload, NonEmptyText, OptimizationObjective, RunDimensionValue, TagName,
+ CommandRecipe, DiagnosticSeverity, EvaluationProtocol, FieldPresence, FieldRole,
+ FieldValueType, FrontierContract, FrontierNote, FrontierVerdict, InferencePolicy,
+ MetricSpec, MetricUnit, MetricValue, NodeAnnotation, NodeClass, NodePayload, NonEmptyText,
+ OptimizationObjective, RunDimensionValue, TagName,
};
fn temp_project_root(label: &str) -> camino::Utf8PathBuf {
@@ -3850,7 +3515,7 @@ mod tests {
}
#[test]
- fn frontier_projection_tracks_initial_champion() -> Result<(), super::StoreError> {
+ fn frontier_projection_tracks_experiment_counts() -> Result<(), super::StoreError> {
let root = temp_project_root("frontier");
let mut store = ProjectStore::init(
&root,
@@ -3874,18 +3539,14 @@ mod tests {
},
promotion_criteria: vec![NonEmptyText::new("strict speedup")?],
},
- initial_checkpoint: Some(super::CheckpointSeed {
- summary: NonEmptyText::new("seed")?,
- snapshot: CheckpointSnapshotRef {
- repo_root: root.clone(),
- worktree_root: root,
- worktree_name: Some(NonEmptyText::new("main")?),
- commit_hash: GitCommitHash::new("0123456789abcdef")?,
- },
- }),
})?;
- assert!(projection.champion_checkpoint_id.is_some());
+ assert_eq!(projection.open_experiment_count, 0);
+ assert_eq!(projection.completed_experiment_count, 0);
+ assert_eq!(projection.verdict_counts.accepted, 0);
+ assert_eq!(projection.verdict_counts.kept, 0);
+ assert_eq!(projection.verdict_counts.parked, 0);
+ assert_eq!(projection.verdict_counts.rejected, 0);
Ok(())
}
@@ -3948,7 +3609,6 @@ mod tests {
},
promotion_criteria: vec![NonEmptyText::new("faster")?],
},
- initial_checkpoint: None,
})?;
let nodes = store.list_nodes(ListNodesQuery {
@@ -4203,15 +3863,8 @@ mod tests {
},
promotion_criteria: vec![NonEmptyText::new("strict speedup")?],
},
- initial_checkpoint: Some(super::CheckpointSeed {
- summary: NonEmptyText::new("seed")?,
- snapshot: checkpoint_snapshot(&root, "aaaaaaaaaaaaaaaa")?,
- }),
})?;
let frontier_id = projection.frontier.id;
- let base_checkpoint_id = projection
- .champion_checkpoint_id
- .ok_or_else(|| super::StoreError::MissingChampionCheckpoint { frontier_id })?;
let _ = store.define_metric(DefineMetricRequest {
key: NonEmptyText::new("wall_clock_s")?,
unit: MetricUnit::Seconds,
@@ -4257,21 +3910,18 @@ mod tests {
})?;
let first_experiment = store.open_experiment(open_experiment_request(
frontier_id,
- base_checkpoint_id,
first_hypothesis.id,
"first experiment",
)?)?;
let second_experiment = store.open_experiment(open_experiment_request(
frontier_id,
- base_checkpoint_id,
second_hypothesis.id,
"second experiment",
)?)?;
- let first_receipt = store.close_experiment(experiment_request(
+ let _first_receipt = store.close_experiment(experiment_request(
&root,
first_experiment.id,
- "bbbbbbbbbbbbbbbb",
"first run",
10.0,
run_dimensions("belt_4x5", 20.0)?,
@@ -4279,7 +3929,6 @@ mod tests {
let second_receipt = store.close_experiment(experiment_request(
&root,
second_experiment.id,
- "cccccccccccccccc",
"second run",
5.0,
run_dimensions("belt_4x5", 60.0)?,
@@ -4334,9 +3983,10 @@ mod tests {
assert_eq!(canonical_best.len(), 1);
assert_eq!(canonical_best[0].value, 5.0);
assert_eq!(
- canonical_best[0].candidate_checkpoint_id,
- second_receipt.checkpoint.id
+ canonical_best[0].experiment_title.as_str(),
+ "second experiment"
);
+ assert_eq!(canonical_best[0].verdict, FrontierVerdict::Kept);
assert_eq!(
canonical_best[0]
.dimensions
@@ -4369,8 +4019,8 @@ mod tests {
Err(super::StoreError::MetricOrderRequired { .. })
));
assert_eq!(
- first_receipt.checkpoint.snapshot.commit_hash.as_str(),
- "bbbbbbbbbbbbbbbb"
+ second_receipt.experiment.title.as_str(),
+ "second experiment"
);
Ok(())
}
@@ -4401,15 +4051,8 @@ mod tests {
},
promotion_criteria: vec![NonEmptyText::new("keep the metric plane queryable")?],
},
- initial_checkpoint: Some(super::CheckpointSeed {
- summary: NonEmptyText::new("seed")?,
- snapshot: checkpoint_snapshot(&root, "aaaaaaaaaaaaaaaa")?,
- }),
})?;
let frontier_id = projection.frontier.id;
- let base_checkpoint_id = projection
- .champion_checkpoint_id
- .ok_or_else(|| super::StoreError::MissingChampionCheckpoint { frontier_id })?;
let hypothesis = store.add_node(CreateNodeRequest {
class: NodeClass::Hypothesis,
frontier_id: Some(frontier_id),
@@ -4425,14 +4068,12 @@ mod tests {
})?;
let experiment = store.open_experiment(open_experiment_request(
frontier_id,
- base_checkpoint_id,
hypothesis.id,
"migration experiment",
)?)?;
let _ = store.close_experiment(experiment_request(
&root,
experiment.id,
- "bbbbbbbbbbbbbbbb",
"migration run",
11.0,
BTreeMap::from([(
@@ -4472,27 +4113,13 @@ mod tests {
Ok(())
}
- fn checkpoint_snapshot(
- root: &camino::Utf8Path,
- commit: &str,
- ) -> Result<CheckpointSnapshotRef, super::StoreError> {
- Ok(CheckpointSnapshotRef {
- repo_root: root.to_path_buf(),
- worktree_root: root.to_path_buf(),
- worktree_name: Some(NonEmptyText::new("main")?),
- commit_hash: GitCommitHash::new(commit)?,
- })
- }
-
fn open_experiment_request(
frontier_id: fidget_spinner_core::FrontierId,
- base_checkpoint_id: fidget_spinner_core::CheckpointId,
hypothesis_node_id: fidget_spinner_core::NodeId,
title: &str,
) -> Result<OpenExperimentRequest, super::StoreError> {
Ok(OpenExperimentRequest {
frontier_id,
- base_checkpoint_id,
hypothesis_node_id,
title: NonEmptyText::new(title)?,
summary: Some(NonEmptyText::new(format!("{title} summary"))?),
@@ -4502,15 +4129,12 @@ mod tests {
fn experiment_request(
root: &camino::Utf8Path,
experiment_id: fidget_spinner_core::ExperimentId,
- candidate_commit: &str,
run_title: &str,
wall_clock_s: f64,
dimensions: BTreeMap<NonEmptyText, RunDimensionValue>,
) -> Result<CloseExperimentRequest, super::StoreError> {
Ok(CloseExperimentRequest {
experiment_id,
- candidate_summary: NonEmptyText::new(format!("candidate {candidate_commit}"))?,
- candidate_snapshot: checkpoint_snapshot(root, candidate_commit)?,
run_title: NonEmptyText::new(run_title)?,
run_summary: Some(NonEmptyText::new("run summary")?),
backend: fidget_spinner_core::ExecutionBackend::WorktreeProcess,
@@ -4520,7 +4144,6 @@ mod tests {
vec![NonEmptyText::new("true")?],
BTreeMap::new(),
)?,
- code_snapshot: None,
primary_metric: MetricValue {
key: NonEmptyText::new("wall_clock_s")?,
value: wall_clock_s,
@@ -4530,7 +4153,7 @@ mod tests {
summary: NonEmptyText::new("note summary")?,
next_hypotheses: Vec::new(),
},
- verdict: FrontierVerdict::KeepOnFrontier,
+ verdict: FrontierVerdict::Kept,
analysis: None,
decision_title: NonEmptyText::new("decision")?,
decision_rationale: NonEmptyText::new("decision rationale")?,