fix: remove workdir management kind

This commit is contained in:
Keisuke Hirata 2026-07-22 20:52:09 +09:00
parent fe2a39c12d
commit 44deacb3f4
No known key found for this signature in database
6 changed files with 82 additions and 172 deletions

View File

@ -123,8 +123,6 @@ pub struct BackendWorkingDirectorySummary {
pub cleanliness: Option<String>, pub cleanliness: Option<String>,
#[serde(default)] #[serde(default)]
pub primary_worker_id: Option<String>, pub primary_worker_id: Option<String>,
#[serde(default)]
pub management_kind: Option<String>,
} }
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Deserialize, PartialEq, Eq)]

View File

@ -156,10 +156,6 @@ pub struct WorkingDirectorySummary {
pub cleanliness: Option<String>, pub cleanliness: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub primary_worker_id: Option<WorkerId>, pub primary_worker_id: Option<WorkerId>,
/// Backend projection metadata. Runtimes leave this absent; Workspace Browser
/// APIs fill it with `backend_managed` or `runtime_unmanaged`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub management_kind: Option<String>,
} }
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]

View File

@ -48,7 +48,6 @@ impl WorkingDirectory {
status: self.status.clone(), status: self.status.clone(),
cleanliness: None, cleanliness: None,
primary_worker_id: None, primary_worker_id: None,
management_kind: None,
} }
} }
} }
@ -222,7 +221,6 @@ impl LocalGitWorktreeMaterializer {
status: WorkingDirectoryStatusKind::Corrupted, status: WorkingDirectoryStatusKind::Corrupted,
cleanliness: Some("unknown".to_string()), cleanliness: Some("unknown".to_string()),
primary_worker_id: None, primary_worker_id: None,
management_kind: None,
}, },
} }
} }

View File

@ -1587,7 +1587,6 @@ fn create_working_directory_for_runtime(
resolved_commit: None, resolved_commit: None,
materialization_status: "pending".to_string(), materialization_status: "pending".to_string(),
cleanliness: "unknown".to_string(), cleanliness: "unknown".to_string(),
management_kind: "backend_managed".to_string(),
created_at: now_registry_timestamp(), created_at: now_registry_timestamp(),
updated_at: now_registry_timestamp(), updated_at: now_registry_timestamp(),
}; };
@ -1610,12 +1609,7 @@ fn create_working_directory_for_runtime(
result.diagnostics, result.diagnostics,
)); ));
}; };
let record = workdir_record_from_summary( let record = workdir_record_from_summary(&api, &runtime_id, &working_directory.summary);
&api,
&runtime_id,
&working_directory.summary,
"backend_managed",
);
api.store.upsert_workdir_registry(&record)?; api.store.upsert_workdir_registry(&record)?;
Ok(Json(BrowserWorkingDirectoryDetailResponse { Ok(Json(BrowserWorkingDirectoryDetailResponse {
workspace_id: api.config.workspace_id.clone(), workspace_id: api.config.workspace_id.clone(),
@ -1634,17 +1628,7 @@ fn working_directory_detail_for_runtime(
.working_directory(runtime_id, working_directory_id) .working_directory(runtime_id, working_directory_id)
.map_err(|err| err.into_error())?; .map_err(|err| err.into_error())?;
if let Some(working_directory) = result.working_directory { if let Some(working_directory) = result.working_directory {
let management_kind = api let record = workdir_record_from_summary(&api, runtime_id, &working_directory.summary);
.store
.get_workdir_registry(&api.config.workspace_id, working_directory_id)?
.map(|record| record.management_kind)
.unwrap_or_else(|| "runtime_unmanaged".to_string());
let record = workdir_record_from_summary(
&api,
runtime_id,
&working_directory.summary,
management_kind.as_str(),
);
api.store.upsert_workdir_registry(&record)?; api.store.upsert_workdir_registry(&record)?;
return Ok(Json(BrowserWorkingDirectoryDetailResponse { return Ok(Json(BrowserWorkingDirectoryDetailResponse {
workspace_id: api.config.workspace_id.clone(), workspace_id: api.config.workspace_id.clone(),
@ -1711,17 +1695,7 @@ fn cleanup_working_directory_for_runtime(
result.diagnostics, result.diagnostics,
)); ));
}; };
let management_kind = api let record = workdir_record_from_summary(&api, runtime_id, &working_directory.summary);
.store
.get_workdir_registry(&api.config.workspace_id, working_directory_id)?
.map(|record| record.management_kind)
.unwrap_or_else(|| "runtime_unmanaged".to_string());
let record = workdir_record_from_summary(
&api,
runtime_id,
&working_directory.summary,
management_kind.as_str(),
);
api.store.upsert_workdir_registry(&record)?; api.store.upsert_workdir_registry(&record)?;
Ok(Json(BrowserWorkingDirectoryDetailResponse { Ok(Json(BrowserWorkingDirectoryDetailResponse {
workspace_id: api.config.workspace_id.clone(), workspace_id: api.config.workspace_id.clone(),
@ -3010,20 +2984,8 @@ async fn create_workspace_worker(
WorkerRegistryDisplayNamePolicy::UseProvided, WorkerRegistryDisplayNamePolicy::UseProvided,
)?; )?;
if let Some(working_directory) = worker.working_directory.as_ref() { if let Some(working_directory) = worker.working_directory.as_ref() {
let management_kind = api let workdir_record =
.store workdir_record_from_summary(&api, worker.runtime_id.as_str(), working_directory);
.get_workdir_registry(
&api.config.workspace_id,
&working_directory.working_directory_id,
)?
.map(|existing| existing.management_kind)
.unwrap_or_else(|| "runtime_unmanaged".to_string());
let workdir_record = workdir_record_from_summary(
&api,
worker.runtime_id.as_str(),
working_directory,
management_kind.as_str(),
);
api.store.upsert_workdir_registry(&workdir_record)?; api.store.upsert_workdir_registry(&workdir_record)?;
link_worker_to_workdir( link_worker_to_workdir(
&api, &api,
@ -3047,7 +3009,6 @@ async fn create_workspace_worker(
&api, &api,
worker.runtime_id.as_str(), worker.runtime_id.as_str(),
&status.summary, &status.summary,
"runtime_unmanaged",
); );
api.store.upsert_workdir_registry(&record)?; api.store.upsert_workdir_registry(&record)?;
} }
@ -4425,7 +4386,7 @@ fn working_directory_summaries(api: &WorkspaceApi) -> ApiResult<Vec<WorkingDirec
let _ = sync_all_runtime_workdir_observations(api); let _ = sync_all_runtime_workdir_observations(api);
let records = api let records = api
.store .store
.list_managed_workdir_registry(&api.config.workspace_id, 200)?; .list_workdir_registry(&api.config.workspace_id, 200)?;
Ok(records Ok(records
.iter() .iter()
.map(workdir_summary_from_record) .map(workdir_summary_from_record)
@ -4619,20 +4580,8 @@ fn sync_worker_observation(
WorkerRegistryDisplayNamePolicy::PreserveExisting, WorkerRegistryDisplayNamePolicy::PreserveExisting,
)?; )?;
if let Some(working_directory) = worker.working_directory.as_ref() { if let Some(working_directory) = worker.working_directory.as_ref() {
let management_kind = api let workdir_record =
.store workdir_record_from_summary(api, worker.runtime_id.as_str(), working_directory);
.get_workdir_registry(
&api.config.workspace_id,
&working_directory.working_directory_id,
)?
.map(|existing| existing.management_kind)
.unwrap_or_else(|| "runtime_unmanaged".to_string());
let workdir_record = workdir_record_from_summary(
api,
worker.runtime_id.as_str(),
working_directory,
management_kind.as_str(),
);
api.store.upsert_workdir_registry(&workdir_record)?; api.store.upsert_workdir_registry(&workdir_record)?;
link_worker_to_workdir(api, &record, &working_directory.working_directory_id)?; link_worker_to_workdir(api, &record, &working_directory.working_directory_id)?;
} }
@ -4663,7 +4612,6 @@ fn upsert_pending_backend_workdir(
resolved_commit: None, resolved_commit: None,
materialization_status: "pending".to_string(), materialization_status: "pending".to_string(),
cleanliness: "unknown".to_string(), cleanliness: "unknown".to_string(),
management_kind: "backend_managed".to_string(),
created_at: timestamp.clone(), created_at: timestamp.clone(),
updated_at: timestamp, updated_at: timestamp,
})?; })?;
@ -4692,12 +4640,7 @@ fn sync_runtime_workdir_observations(
&api.config.workspace_id, &api.config.workspace_id,
&status.summary.working_directory_id, &status.summary.working_directory_id,
)?; )?;
let management_kind = existing let mut record = workdir_record_from_summary(api, runtime_id, &status.summary);
.as_ref()
.map(|existing| existing.management_kind.clone())
.unwrap_or_else(|| "runtime_unmanaged".to_string());
let mut record =
workdir_record_from_summary(api, runtime_id, &status.summary, management_kind.as_str());
preserve_workdir_identity_for_corrupted_summary(&mut record, existing.as_ref()); preserve_workdir_identity_for_corrupted_summary(&mut record, existing.as_ref());
api.store.upsert_workdir_registry(&record)?; api.store.upsert_workdir_registry(&record)?;
} }
@ -4719,13 +4662,8 @@ fn sync_runtime_workdir_observations(
record.workdir_id.as_str(), record.workdir_id.as_str(),
)?; )?;
} else { } else {
let management_kind = record.management_kind.clone(); let mut updated =
let mut updated = workdir_record_from_summary( workdir_record_from_summary(api, runtime_id, &status.summary);
api,
runtime_id,
&status.summary,
management_kind.as_str(),
);
preserve_workdir_identity_for_corrupted_summary( preserve_workdir_identity_for_corrupted_summary(
&mut updated, &mut updated,
Some(&record), Some(&record),
@ -4792,17 +4730,7 @@ fn sync_linked_workdir_after_worker_stop(
.working_directory(runtime_id, link.workdir_id.as_str()) .working_directory(runtime_id, link.workdir_id.as_str())
.map_err(|err| err.into_error())?; .map_err(|err| err.into_error())?;
if let Some(status) = result.working_directory { if let Some(status) = result.working_directory {
let management_kind = api let record = workdir_record_from_summary(api, runtime_id, &status.summary);
.store
.get_workdir_registry(&api.config.workspace_id, link.workdir_id.as_str())?
.map(|record| record.management_kind)
.unwrap_or_else(|| "runtime_unmanaged".to_string());
let record = workdir_record_from_summary(
api,
runtime_id,
&status.summary,
management_kind.as_str(),
);
api.store.upsert_workdir_registry(&record)?; api.store.upsert_workdir_registry(&record)?;
} else if let Some(mut record) = api } else if let Some(mut record) = api
.store .store
@ -4822,7 +4750,6 @@ fn workdir_record_from_summary(
api: &WorkspaceApi, api: &WorkspaceApi,
runtime_id: &str, runtime_id: &str,
summary: &WorkingDirectorySummary, summary: &WorkingDirectorySummary,
management_kind: &str,
) -> WorkdirRegistryRecord { ) -> WorkdirRegistryRecord {
let timestamp = now_registry_timestamp(); let timestamp = now_registry_timestamp();
WorkdirRegistryRecord { WorkdirRegistryRecord {
@ -4844,7 +4771,6 @@ fn workdir_record_from_summary(
.cleanliness .cleanliness
.clone() .clone()
.unwrap_or_else(|| "unknown".to_string()), .unwrap_or_else(|| "unknown".to_string()),
management_kind: management_kind.to_string(),
created_at: timestamp.clone(), created_at: timestamp.clone(),
updated_at: timestamp, updated_at: timestamp,
} }
@ -4895,7 +4821,6 @@ fn workdir_summary_from_record(record: &WorkdirRegistryRecord) -> WorkingDirecto
status, status,
cleanliness: Some(record.cleanliness.clone()), cleanliness: Some(record.cleanliness.clone()),
primary_worker_id: None, primary_worker_id: None,
management_kind: Some(record.management_kind.clone()),
} }
} }
@ -5517,7 +5442,6 @@ mod tests {
resolved_commit: Some("abcdef".to_string()), resolved_commit: Some("abcdef".to_string()),
materialization_status: "missing".to_string(), materialization_status: "missing".to_string(),
cleanliness: "clean".to_string(), cleanliness: "clean".to_string(),
management_kind: "backend_managed".to_string(),
created_at: "1".to_string(), created_at: "1".to_string(),
updated_at: "3".to_string(), updated_at: "3".to_string(),
}; };
@ -5538,22 +5462,13 @@ mod tests {
projected.working_directory.as_ref().unwrap().status, projected.working_directory.as_ref().unwrap().status,
WorkingDirectoryStatusKind::NotFound WorkingDirectoryStatusKind::NotFound
); );
assert_eq!(
projected
.working_directory
.as_ref()
.unwrap()
.management_kind
.as_deref(),
Some("backend_managed")
);
let serialized = serde_json::to_string(&projected).unwrap(); let serialized = serde_json::to_string(&projected).unwrap();
assert!(!serialized.contains("/tmp/")); assert!(!serialized.contains("/tmp/"));
assert!(!serialized.contains("materialized_path")); assert!(!serialized.contains("materialized_path"));
} }
#[tokio::test] #[tokio::test]
async fn workspace_managed_workdir_summaries_exclude_runtime_unmanaged_rows() { async fn workspace_workdir_summaries_include_runtime_observed_rows() {
let dir = tempfile::tempdir().unwrap(); let dir = tempfile::tempdir().unwrap();
let api = test_api(dir.path()).await; let api = test_api(dir.path()).await;
api.store api.store
@ -5566,7 +5481,6 @@ mod tests {
resolved_commit: None, resolved_commit: None,
materialization_status: "present".to_string(), materialization_status: "present".to_string(),
cleanliness: "clean".to_string(), cleanliness: "clean".to_string(),
management_kind: "backend_managed".to_string(),
created_at: "1".to_string(), created_at: "1".to_string(),
updated_at: "1".to_string(), updated_at: "1".to_string(),
}) })
@ -5581,29 +5495,29 @@ mod tests {
resolved_commit: None, resolved_commit: None,
materialization_status: "present".to_string(), materialization_status: "present".to_string(),
cleanliness: "unknown".to_string(), cleanliness: "unknown".to_string(),
management_kind: "runtime_unmanaged".to_string(),
created_at: "1".to_string(), created_at: "1".to_string(),
updated_at: "2".to_string(), updated_at: "2".to_string(),
}) })
.unwrap(); .unwrap();
let managed = working_directory_summaries(&api) let summaries = working_directory_summaries(&api)
.unwrap_or_else(|err| panic!("working_directory_summaries failed: {}", err.error)); .unwrap_or_else(|err| panic!("working_directory_summaries failed: {}", err.error));
assert_eq!(managed.len(), 1); let ids = summaries
assert_eq!(managed[0].working_directory_id, "managed"); .iter()
assert_eq!( .map(|summary| summary.working_directory_id.as_str())
managed[0].management_kind.as_deref(), .collect::<Vec<_>>();
Some("backend_managed") assert!(ids.contains(&"managed"));
); assert!(ids.contains(&"runtime-direct"));
let (runtime_projection, _) = let (runtime_projection, _) =
runtime_working_directory_summaries(&api, EMBEDDED_WORKER_RUNTIME_ID).unwrap_or_else( runtime_working_directory_summaries(&api, EMBEDDED_WORKER_RUNTIME_ID).unwrap_or_else(
|err| panic!("runtime_working_directory_summaries failed: {}", err.error), |err| panic!("runtime_working_directory_summaries failed: {}", err.error),
); );
assert!(runtime_projection.iter().any(|summary| { assert!(
summary.working_directory_id == "runtime-direct" runtime_projection
&& summary.management_kind.as_deref() == Some("runtime_unmanaged") .iter()
})); .any(|summary| summary.working_directory_id == "runtime-direct")
);
} }
#[test] #[test]
fn unmanaged_runtime_workdir_projection_is_typed_and_diagnostic_safe() { fn unmanaged_runtime_workdir_projection_is_typed_and_diagnostic_safe() {
@ -5616,7 +5530,6 @@ mod tests {
resolved_commit: None, resolved_commit: None,
materialization_status: "present".to_string(), materialization_status: "present".to_string(),
cleanliness: "unknown".to_string(), cleanliness: "unknown".to_string(),
management_kind: "runtime_unmanaged".to_string(),
created_at: "1".to_string(), created_at: "1".to_string(),
updated_at: "2".to_string(), updated_at: "2".to_string(),
}; };
@ -5624,10 +5537,6 @@ mod tests {
let projected = workdir_summary_from_record(&workdir); let projected = workdir_summary_from_record(&workdir);
assert_eq!(projected.status, WorkingDirectoryStatusKind::Active); assert_eq!(projected.status, WorkingDirectoryStatusKind::Active);
assert_eq!(
projected.management_kind.as_deref(),
Some("runtime_unmanaged")
);
let serialized = serde_json::to_string(&projected).unwrap(); let serialized = serde_json::to_string(&projected).unwrap();
assert!(!serialized.contains("/tmp/")); assert!(!serialized.contains("/tmp/"));
assert!(!serialized.contains("materialized_path")); assert!(!serialized.contains("materialized_path"));
@ -6227,7 +6136,6 @@ mod tests {
workdir_id: workdir_id.to_string(), workdir_id: workdir_id.to_string(),
runtime_id: "runtime-test".to_string(), runtime_id: "runtime-test".to_string(),
repository_id: "repo-test".to_string(), repository_id: "repo-test".to_string(),
management_kind: "backend_managed".to_string(),
selector: Some("HEAD".to_string()), selector: Some("HEAD".to_string()),
resolved_commit: None, resolved_commit: None,
materialization_status: status.to_string(), materialization_status: status.to_string(),

View File

@ -47,6 +47,11 @@ const MIGRATIONS: &[Migration] = &[
name: "add workdir runtime observation states", name: "add workdir runtime observation states",
apply: add_workdir_runtime_observation_states, apply: add_workdir_runtime_observation_states,
}, },
Migration {
version: 7,
name: "remove workdir registry management kind",
apply: remove_workdir_registry_management_kind_column,
},
]; ];
struct Migration { struct Migration {
@ -91,8 +96,6 @@ pub struct WorkdirRegistryRecord {
pub resolved_commit: Option<String>, pub resolved_commit: Option<String>,
pub materialization_status: String, pub materialization_status: String,
pub cleanliness: String, pub cleanliness: String,
/// `backend_managed` rows are authored by this Backend; `runtime_unmanaged` is for diagnostics only.
pub management_kind: String,
pub created_at: String, pub created_at: String,
pub updated_at: String, pub updated_at: String,
} }
@ -152,11 +155,6 @@ pub trait ControlPlaneStore: Send + Sync {
workspace_id: &str, workspace_id: &str,
limit: usize, limit: usize,
) -> Result<Vec<WorkdirRegistryRecord>>; ) -> Result<Vec<WorkdirRegistryRecord>>;
fn list_managed_workdir_registry(
&self,
workspace_id: &str,
limit: usize,
) -> Result<Vec<WorkdirRegistryRecord>>;
fn delete_workdir_registry(&self, workspace_id: &str, workdir_id: &str) -> Result<bool>; fn delete_workdir_registry(&self, workspace_id: &str, workdir_id: &str) -> Result<bool>;
fn upsert_worker_workdir_link(&self, record: &WorkerWorkdirLinkRecord) -> Result<()>; fn upsert_worker_workdir_link(&self, record: &WorkerWorkdirLinkRecord) -> Result<()>;
@ -378,8 +376,8 @@ impl ControlPlaneStore for SqliteWorkspaceStore {
conn.execute( conn.execute(
r#"INSERT INTO workdir_registry ( r#"INSERT INTO workdir_registry (
workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit, workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit,
materialization_status, cleanliness, management_kind, created_at, updated_at materialization_status, cleanliness, created_at, updated_at
) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11) ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
ON CONFLICT(workspace_id, workdir_id) DO UPDATE SET ON CONFLICT(workspace_id, workdir_id) DO UPDATE SET
runtime_id = excluded.runtime_id, runtime_id = excluded.runtime_id,
repository_id = excluded.repository_id, repository_id = excluded.repository_id,
@ -387,7 +385,6 @@ impl ControlPlaneStore for SqliteWorkspaceStore {
resolved_commit = excluded.resolved_commit, resolved_commit = excluded.resolved_commit,
materialization_status = excluded.materialization_status, materialization_status = excluded.materialization_status,
cleanliness = excluded.cleanliness, cleanliness = excluded.cleanliness,
management_kind = excluded.management_kind,
updated_at = excluded.updated_at"#, updated_at = excluded.updated_at"#,
params![ params![
record.workspace_id, record.workspace_id,
@ -398,7 +395,6 @@ impl ControlPlaneStore for SqliteWorkspaceStore {
record.resolved_commit, record.resolved_commit,
record.materialization_status, record.materialization_status,
record.cleanliness, record.cleanliness,
record.management_kind,
record.created_at, record.created_at,
record.updated_at, record.updated_at,
], ],
@ -442,22 +438,6 @@ impl ControlPlaneStore for SqliteWorkspaceStore {
}) })
} }
fn list_managed_workdir_registry(
&self,
workspace_id: &str,
limit: usize,
) -> Result<Vec<WorkdirRegistryRecord>> {
self.with_conn(|conn| {
let sql = workdir_registry_select_sql(
"WHERE workspace_id = ?1 AND management_kind = 'backend_managed' ORDER BY updated_at DESC LIMIT ?2",
);
let mut stmt = conn.prepare(sql.as_str())?;
let rows = stmt.query_map(params![workspace_id, limit as i64], read_workdir_registry_record)?;
rows.collect::<std::result::Result<Vec<_>, _>>()
.map_err(Error::from)
})
}
fn delete_workdir_registry(&self, workspace_id: &str, workdir_id: &str) -> Result<bool> { fn delete_workdir_registry(&self, workspace_id: &str, workdir_id: &str) -> Result<bool> {
self.with_conn(|conn| { self.with_conn(|conn| {
let changed = conn.execute( let changed = conn.execute(
@ -577,7 +557,7 @@ fn read_worker_registry_record(row: &rusqlite::Row<'_>) -> rusqlite::Result<Work
fn workdir_registry_select_sql(where_clause: &str) -> String { fn workdir_registry_select_sql(where_clause: &str) -> String {
format!( format!(
"SELECT workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit, \ "SELECT workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit, \
materialization_status, cleanliness, management_kind, created_at, updated_at \ materialization_status, cleanliness, created_at, updated_at \
FROM workdir_registry {where_clause}" FROM workdir_registry {where_clause}"
) )
} }
@ -594,9 +574,8 @@ fn read_workdir_registry_record(
resolved_commit: row.get(5)?, resolved_commit: row.get(5)?,
materialization_status: row.get(6)?, materialization_status: row.get(6)?,
cleanliness: row.get(7)?, cleanliness: row.get(7)?,
management_kind: row.get(8)?, created_at: row.get(8)?,
created_at: row.get(9)?, updated_at: row.get(9)?,
updated_at: row.get(10)?,
}) })
} }
@ -629,7 +608,6 @@ CREATE TABLE IF NOT EXISTS workdir_registry (
resolved_commit TEXT, resolved_commit TEXT,
materialization_status TEXT NOT NULL CHECK (materialization_status IN ('pending', 'present', 'not_found', 'corrupted', 'unknown', 'failed')), materialization_status TEXT NOT NULL CHECK (materialization_status IN ('pending', 'present', 'not_found', 'corrupted', 'unknown', 'failed')),
cleanliness TEXT NOT NULL CHECK (cleanliness IN ('clean', 'dirty', 'unknown')), cleanliness TEXT NOT NULL CHECK (cleanliness IN ('clean', 'dirty', 'unknown')),
management_kind TEXT NOT NULL CHECK (management_kind IN ('backend_managed', 'runtime_unmanaged')),
created_at TEXT NOT NULL, created_at TEXT NOT NULL,
updated_at TEXT NOT NULL, updated_at TEXT NOT NULL,
PRIMARY KEY (workspace_id, workdir_id), PRIMARY KEY (workspace_id, workdir_id),
@ -948,7 +926,6 @@ fn add_workdir_runtime_observation_states(conn: &Connection) -> Result<()> {
resolved_commit TEXT, resolved_commit TEXT,
materialization_status TEXT NOT NULL CHECK (materialization_status IN ('pending', 'present', 'not_found', 'corrupted', 'unknown', 'failed')), materialization_status TEXT NOT NULL CHECK (materialization_status IN ('pending', 'present', 'not_found', 'corrupted', 'unknown', 'failed')),
cleanliness TEXT NOT NULL CHECK (cleanliness IN ('clean', 'dirty', 'unknown')), cleanliness TEXT NOT NULL CHECK (cleanliness IN ('clean', 'dirty', 'unknown')),
management_kind TEXT NOT NULL CHECK (management_kind IN ('backend_managed', 'runtime_unmanaged')),
created_at TEXT NOT NULL, created_at TEXT NOT NULL,
updated_at TEXT NOT NULL, updated_at TEXT NOT NULL,
PRIMARY KEY (workspace_id, workdir_id), PRIMARY KEY (workspace_id, workdir_id),
@ -956,7 +933,7 @@ fn add_workdir_runtime_observation_states(conn: &Connection) -> Result<()> {
); );
INSERT OR REPLACE INTO workdir_registry_v6 ( INSERT OR REPLACE INTO workdir_registry_v6 (
workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit, workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit,
materialization_status, cleanliness, management_kind, created_at, updated_at materialization_status, cleanliness, created_at, updated_at
) )
SELECT SELECT
workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit, workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit,
@ -965,7 +942,7 @@ fn add_workdir_runtime_observation_states(conn: &Connection) -> Result<()> {
WHEN 'removed' THEN 'not_found' WHEN 'removed' THEN 'not_found'
ELSE materialization_status ELSE materialization_status
END, END,
cleanliness, management_kind, created_at, updated_at cleanliness, created_at, updated_at
FROM workdir_registry; FROM workdir_registry;
DROP TABLE workdir_registry; DROP TABLE workdir_registry;
ALTER TABLE workdir_registry_v6 RENAME TO workdir_registry; ALTER TABLE workdir_registry_v6 RENAME TO workdir_registry;
@ -976,6 +953,48 @@ fn add_workdir_runtime_observation_states(conn: &Connection) -> Result<()> {
Ok(()) Ok(())
} }
fn remove_workdir_registry_management_kind_column(conn: &Connection) -> Result<()> {
if !table_exists(conn, "workdir_registry")?
|| !table_columns(conn, "workdir_registry")?
.iter()
.any(|column| column == "management_kind")
{
return Ok(());
}
conn.execute_batch(
r#"
CREATE TABLE workdir_registry_v7 (
workspace_id TEXT NOT NULL,
workdir_id TEXT NOT NULL,
runtime_id TEXT NOT NULL,
repository_id TEXT NOT NULL,
selector TEXT,
resolved_commit TEXT,
materialization_status TEXT NOT NULL CHECK (materialization_status IN ('pending', 'present', 'not_found', 'corrupted', 'unknown', 'failed')),
cleanliness TEXT NOT NULL CHECK (cleanliness IN ('clean', 'dirty', 'unknown')),
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (workspace_id, workdir_id),
FOREIGN KEY (workspace_id) REFERENCES workspaces(workspace_id) ON DELETE CASCADE
);
INSERT OR REPLACE INTO workdir_registry_v7 (
workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit,
materialization_status, cleanliness, created_at, updated_at
)
SELECT
workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit,
materialization_status, cleanliness, created_at, updated_at
FROM workdir_registry;
DROP TABLE workdir_registry;
ALTER TABLE workdir_registry_v7 RENAME TO workdir_registry;
CREATE INDEX IF NOT EXISTS idx_workdir_registry_workspace_updated
ON workdir_registry(workspace_id, updated_at DESC);
"#,
)?;
Ok(())
}
fn create_schema_v0_tables(conn: &Connection) -> Result<()> { fn create_schema_v0_tables(conn: &Connection) -> Result<()> {
conn.execute_batch( conn.execute_batch(
r#" r#"
@ -1199,7 +1218,7 @@ mod tests {
let db = dir.path().join("control-plane.sqlite"); let db = dir.path().join("control-plane.sqlite");
let store = SqliteWorkspaceStore::open(&db).unwrap(); let store = SqliteWorkspaceStore::open(&db).unwrap();
assert_eq!(store.schema_version().await.unwrap(), 6); assert_eq!(store.schema_version().await.unwrap(), 7);
let record = WorkspaceRecord { let record = WorkspaceRecord {
workspace_id: "local-dev".to_string(), workspace_id: "local-dev".to_string(),
@ -1211,7 +1230,7 @@ mod tests {
store.upsert_workspace(&record).await.unwrap(); store.upsert_workspace(&record).await.unwrap();
let reopened = SqliteWorkspaceStore::open(&db).unwrap(); let reopened = SqliteWorkspaceStore::open(&db).unwrap();
assert_eq!(reopened.schema_version().await.unwrap(), 6); assert_eq!(reopened.schema_version().await.unwrap(), 7);
assert_eq!( assert_eq!(
reopened.get_workspace("local-dev").await.unwrap(), reopened.get_workspace("local-dev").await.unwrap(),
Some(record) Some(record)
@ -1420,7 +1439,7 @@ mod tests {
.unwrap(); .unwrap();
let store = SqliteWorkspaceStore::from_connection(conn).unwrap(); let store = SqliteWorkspaceStore::from_connection(conn).unwrap();
assert_eq!(store.schema_version().await.unwrap(), 6); assert_eq!(store.schema_version().await.unwrap(), 7);
store store
.with_conn(|conn| { .with_conn(|conn| {
@ -1550,7 +1569,6 @@ mod tests {
resolved_commit: Some("abcdef".to_string()), resolved_commit: Some("abcdef".to_string()),
materialization_status: "not_found".to_string(), materialization_status: "not_found".to_string(),
cleanliness: "clean".to_string(), cleanliness: "clean".to_string(),
management_kind: "backend_managed".to_string(),
created_at: "2".to_string(), created_at: "2".to_string(),
updated_at: "3".to_string(), updated_at: "3".to_string(),
}; };
@ -1564,7 +1582,6 @@ mod tests {
resolved_commit: Some("123456".to_string()), resolved_commit: Some("123456".to_string()),
materialization_status: "present".to_string(), materialization_status: "present".to_string(),
cleanliness: "unknown".to_string(), cleanliness: "unknown".to_string(),
management_kind: "runtime_unmanaged".to_string(),
created_at: "3".to_string(), created_at: "3".to_string(),
updated_at: "4".to_string(), updated_at: "4".to_string(),
}; };
@ -1597,12 +1614,6 @@ mod tests {
store.list_workdir_registry("local-dev", 10).unwrap(), store.list_workdir_registry("local-dev", 10).unwrap(),
vec![unmanaged_workdir.clone(), workdir.clone()] vec![unmanaged_workdir.clone(), workdir.clone()]
); );
assert_eq!(
store
.list_managed_workdir_registry("local-dev", 10)
.unwrap(),
vec![workdir]
);
assert_eq!( assert_eq!(
store store
.list_worker_workdir_links("local-dev", "embedded", 1) .list_worker_workdir_links("local-dev", "embedded", 1)

View File

@ -125,7 +125,6 @@ export type WorkingDirectorySummary = {
status: string; status: string;
cleanliness?: string | null; cleanliness?: string | null;
primary_worker_id?: number | null; primary_worker_id?: number | null;
management_kind?: "backend_managed" | "runtime_unmanaged" | string | null;
cleanup_target: { cleanup_target: {
kind: string; kind: string;
working_directory_id: string; working_directory_id: string;