diff --git a/crates/client/src/backend_runtime.rs b/crates/client/src/backend_runtime.rs index 47f0aceb..b75faadd 100644 --- a/crates/client/src/backend_runtime.rs +++ b/crates/client/src/backend_runtime.rs @@ -123,8 +123,6 @@ pub struct BackendWorkingDirectorySummary { pub cleanliness: Option, #[serde(default)] pub primary_worker_id: Option, - #[serde(default)] - pub management_kind: Option, } #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] diff --git a/crates/worker-runtime/src/catalog.rs b/crates/worker-runtime/src/catalog.rs index cb8912c2..7d67edc8 100644 --- a/crates/worker-runtime/src/catalog.rs +++ b/crates/worker-runtime/src/catalog.rs @@ -156,10 +156,6 @@ pub struct WorkingDirectorySummary { pub cleanliness: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub primary_worker_id: Option, - /// 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, } #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] diff --git a/crates/worker-runtime/src/working_directory.rs b/crates/worker-runtime/src/working_directory.rs index 151b57e9..1579a094 100644 --- a/crates/worker-runtime/src/working_directory.rs +++ b/crates/worker-runtime/src/working_directory.rs @@ -48,7 +48,6 @@ impl WorkingDirectory { status: self.status.clone(), cleanliness: None, primary_worker_id: None, - management_kind: None, } } } @@ -222,7 +221,6 @@ impl LocalGitWorktreeMaterializer { status: WorkingDirectoryStatusKind::Corrupted, cleanliness: Some("unknown".to_string()), primary_worker_id: None, - management_kind: None, }, } } diff --git a/crates/workspace-server/src/server.rs b/crates/workspace-server/src/server.rs index 0d2992ed..38cd8d63 100644 --- a/crates/workspace-server/src/server.rs +++ b/crates/workspace-server/src/server.rs @@ -1587,7 +1587,6 @@ fn create_working_directory_for_runtime( resolved_commit: None, materialization_status: "pending".to_string(), cleanliness: "unknown".to_string(), - management_kind: "backend_managed".to_string(), created_at: now_registry_timestamp(), updated_at: now_registry_timestamp(), }; @@ -1610,12 +1609,7 @@ fn create_working_directory_for_runtime( result.diagnostics, )); }; - let record = workdir_record_from_summary( - &api, - &runtime_id, - &working_directory.summary, - "backend_managed", - ); + let record = workdir_record_from_summary(&api, &runtime_id, &working_directory.summary); api.store.upsert_workdir_registry(&record)?; Ok(Json(BrowserWorkingDirectoryDetailResponse { workspace_id: api.config.workspace_id.clone(), @@ -1634,17 +1628,7 @@ fn working_directory_detail_for_runtime( .working_directory(runtime_id, working_directory_id) .map_err(|err| err.into_error())?; if let Some(working_directory) = result.working_directory { - let management_kind = api - .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(), - ); + let record = workdir_record_from_summary(&api, runtime_id, &working_directory.summary); api.store.upsert_workdir_registry(&record)?; return Ok(Json(BrowserWorkingDirectoryDetailResponse { workspace_id: api.config.workspace_id.clone(), @@ -1711,17 +1695,7 @@ fn cleanup_working_directory_for_runtime( result.diagnostics, )); }; - let management_kind = api - .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(), - ); + let record = workdir_record_from_summary(&api, runtime_id, &working_directory.summary); api.store.upsert_workdir_registry(&record)?; Ok(Json(BrowserWorkingDirectoryDetailResponse { workspace_id: api.config.workspace_id.clone(), @@ -3010,20 +2984,8 @@ async fn create_workspace_worker( WorkerRegistryDisplayNamePolicy::UseProvided, )?; if let Some(working_directory) = worker.working_directory.as_ref() { - let management_kind = api - .store - .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(), - ); + let workdir_record = + workdir_record_from_summary(&api, worker.runtime_id.as_str(), working_directory); api.store.upsert_workdir_registry(&workdir_record)?; link_worker_to_workdir( &api, @@ -3047,7 +3009,6 @@ async fn create_workspace_worker( &api, worker.runtime_id.as_str(), &status.summary, - "runtime_unmanaged", ); api.store.upsert_workdir_registry(&record)?; } @@ -4425,7 +4386,7 @@ fn working_directory_summaries(api: &WorkspaceApi) -> ApiResult WorkdirRegistryRecord { let timestamp = now_registry_timestamp(); WorkdirRegistryRecord { @@ -4844,7 +4771,6 @@ fn workdir_record_from_summary( .cleanliness .clone() .unwrap_or_else(|| "unknown".to_string()), - management_kind: management_kind.to_string(), created_at: timestamp.clone(), updated_at: timestamp, } @@ -4895,7 +4821,6 @@ fn workdir_summary_from_record(record: &WorkdirRegistryRecord) -> WorkingDirecto status, cleanliness: Some(record.cleanliness.clone()), primary_worker_id: None, - management_kind: Some(record.management_kind.clone()), } } @@ -5517,7 +5442,6 @@ mod tests { resolved_commit: Some("abcdef".to_string()), materialization_status: "missing".to_string(), cleanliness: "clean".to_string(), - management_kind: "backend_managed".to_string(), created_at: "1".to_string(), updated_at: "3".to_string(), }; @@ -5538,22 +5462,13 @@ mod tests { projected.working_directory.as_ref().unwrap().status, 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(); assert!(!serialized.contains("/tmp/")); assert!(!serialized.contains("materialized_path")); } #[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 api = test_api(dir.path()).await; api.store @@ -5566,7 +5481,6 @@ mod tests { resolved_commit: None, materialization_status: "present".to_string(), cleanliness: "clean".to_string(), - management_kind: "backend_managed".to_string(), created_at: "1".to_string(), updated_at: "1".to_string(), }) @@ -5581,29 +5495,29 @@ mod tests { resolved_commit: None, materialization_status: "present".to_string(), cleanliness: "unknown".to_string(), - management_kind: "runtime_unmanaged".to_string(), created_at: "1".to_string(), updated_at: "2".to_string(), }) .unwrap(); - let managed = working_directory_summaries(&api) + let summaries = working_directory_summaries(&api) .unwrap_or_else(|err| panic!("working_directory_summaries failed: {}", err.error)); - assert_eq!(managed.len(), 1); - assert_eq!(managed[0].working_directory_id, "managed"); - assert_eq!( - managed[0].management_kind.as_deref(), - Some("backend_managed") - ); + let ids = summaries + .iter() + .map(|summary| summary.working_directory_id.as_str()) + .collect::>(); + assert!(ids.contains(&"managed")); + assert!(ids.contains(&"runtime-direct")); let (runtime_projection, _) = runtime_working_directory_summaries(&api, EMBEDDED_WORKER_RUNTIME_ID).unwrap_or_else( |err| panic!("runtime_working_directory_summaries failed: {}", err.error), ); - assert!(runtime_projection.iter().any(|summary| { - summary.working_directory_id == "runtime-direct" - && summary.management_kind.as_deref() == Some("runtime_unmanaged") - })); + assert!( + runtime_projection + .iter() + .any(|summary| summary.working_directory_id == "runtime-direct") + ); } #[test] fn unmanaged_runtime_workdir_projection_is_typed_and_diagnostic_safe() { @@ -5616,7 +5530,6 @@ mod tests { resolved_commit: None, materialization_status: "present".to_string(), cleanliness: "unknown".to_string(), - management_kind: "runtime_unmanaged".to_string(), created_at: "1".to_string(), updated_at: "2".to_string(), }; @@ -5624,10 +5537,6 @@ mod tests { let projected = workdir_summary_from_record(&workdir); assert_eq!(projected.status, WorkingDirectoryStatusKind::Active); - assert_eq!( - projected.management_kind.as_deref(), - Some("runtime_unmanaged") - ); let serialized = serde_json::to_string(&projected).unwrap(); assert!(!serialized.contains("/tmp/")); assert!(!serialized.contains("materialized_path")); @@ -6227,7 +6136,6 @@ mod tests { workdir_id: workdir_id.to_string(), runtime_id: "runtime-test".to_string(), repository_id: "repo-test".to_string(), - management_kind: "backend_managed".to_string(), selector: Some("HEAD".to_string()), resolved_commit: None, materialization_status: status.to_string(), diff --git a/crates/workspace-server/src/store.rs b/crates/workspace-server/src/store.rs index 1e6cdc54..64b198e7 100644 --- a/crates/workspace-server/src/store.rs +++ b/crates/workspace-server/src/store.rs @@ -47,6 +47,11 @@ const MIGRATIONS: &[Migration] = &[ name: "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 { @@ -91,8 +96,6 @@ pub struct WorkdirRegistryRecord { pub resolved_commit: Option, pub materialization_status: 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 updated_at: String, } @@ -152,11 +155,6 @@ pub trait ControlPlaneStore: Send + Sync { workspace_id: &str, limit: usize, ) -> Result>; - fn list_managed_workdir_registry( - &self, - workspace_id: &str, - limit: usize, - ) -> Result>; fn delete_workdir_registry(&self, workspace_id: &str, workdir_id: &str) -> Result; fn upsert_worker_workdir_link(&self, record: &WorkerWorkdirLinkRecord) -> Result<()>; @@ -378,8 +376,8 @@ impl ControlPlaneStore for SqliteWorkspaceStore { conn.execute( r#"INSERT INTO workdir_registry ( workspace_id, workdir_id, runtime_id, repository_id, selector, resolved_commit, - materialization_status, cleanliness, management_kind, created_at, updated_at - ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11) + materialization_status, cleanliness, created_at, updated_at + ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10) ON CONFLICT(workspace_id, workdir_id) DO UPDATE SET runtime_id = excluded.runtime_id, repository_id = excluded.repository_id, @@ -387,7 +385,6 @@ impl ControlPlaneStore for SqliteWorkspaceStore { resolved_commit = excluded.resolved_commit, materialization_status = excluded.materialization_status, cleanliness = excluded.cleanliness, - management_kind = excluded.management_kind, updated_at = excluded.updated_at"#, params![ record.workspace_id, @@ -398,7 +395,6 @@ impl ControlPlaneStore for SqliteWorkspaceStore { record.resolved_commit, record.materialization_status, record.cleanliness, - record.management_kind, record.created_at, record.updated_at, ], @@ -442,22 +438,6 @@ impl ControlPlaneStore for SqliteWorkspaceStore { }) } - fn list_managed_workdir_registry( - &self, - workspace_id: &str, - limit: usize, - ) -> Result> { - 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::, _>>() - .map_err(Error::from) - }) - } - fn delete_workdir_registry(&self, workspace_id: &str, workdir_id: &str) -> Result { self.with_conn(|conn| { let changed = conn.execute( @@ -577,7 +557,7 @@ fn read_worker_registry_record(row: &rusqlite::Row<'_>) -> rusqlite::Result String { format!( "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}" ) } @@ -594,9 +574,8 @@ fn read_workdir_registry_record( resolved_commit: row.get(5)?, materialization_status: row.get(6)?, cleanliness: row.get(7)?, - management_kind: row.get(8)?, - created_at: row.get(9)?, - updated_at: row.get(10)?, + created_at: row.get(8)?, + updated_at: row.get(9)?, }) } @@ -629,7 +608,6 @@ CREATE TABLE IF NOT EXISTS workdir_registry ( 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')), - management_kind TEXT NOT NULL CHECK (management_kind IN ('backend_managed', 'runtime_unmanaged')), created_at TEXT NOT NULL, updated_at TEXT NOT NULL, PRIMARY KEY (workspace_id, workdir_id), @@ -948,7 +926,6 @@ fn add_workdir_runtime_observation_states(conn: &Connection) -> Result<()> { 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')), - management_kind TEXT NOT NULL CHECK (management_kind IN ('backend_managed', 'runtime_unmanaged')), created_at TEXT NOT NULL, updated_at TEXT NOT NULL, 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 ( 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 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' ELSE materialization_status END, - cleanliness, management_kind, created_at, updated_at + cleanliness, created_at, updated_at FROM workdir_registry; DROP TABLE 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(()) } +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<()> { conn.execute_batch( r#" @@ -1199,7 +1218,7 @@ mod tests { let db = dir.path().join("control-plane.sqlite"); 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 { workspace_id: "local-dev".to_string(), @@ -1211,7 +1230,7 @@ mod tests { store.upsert_workspace(&record).await.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!( reopened.get_workspace("local-dev").await.unwrap(), Some(record) @@ -1420,7 +1439,7 @@ mod tests { .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 .with_conn(|conn| { @@ -1550,7 +1569,6 @@ mod tests { resolved_commit: Some("abcdef".to_string()), materialization_status: "not_found".to_string(), cleanliness: "clean".to_string(), - management_kind: "backend_managed".to_string(), created_at: "2".to_string(), updated_at: "3".to_string(), }; @@ -1564,7 +1582,6 @@ mod tests { resolved_commit: Some("123456".to_string()), materialization_status: "present".to_string(), cleanliness: "unknown".to_string(), - management_kind: "runtime_unmanaged".to_string(), created_at: "3".to_string(), updated_at: "4".to_string(), }; @@ -1597,12 +1614,6 @@ mod tests { store.list_workdir_registry("local-dev", 10).unwrap(), vec![unmanaged_workdir.clone(), workdir.clone()] ); - assert_eq!( - store - .list_managed_workdir_registry("local-dev", 10) - .unwrap(), - vec![workdir] - ); assert_eq!( store .list_worker_workdir_links("local-dev", "embedded", 1) diff --git a/web/workspace/src/lib/workspace/sidebar/types.ts b/web/workspace/src/lib/workspace/sidebar/types.ts index b7bed78e..7091da1f 100644 --- a/web/workspace/src/lib/workspace/sidebar/types.ts +++ b/web/workspace/src/lib/workspace/sidebar/types.ts @@ -125,7 +125,6 @@ export type WorkingDirectorySummary = { status: string; cleanliness?: string | null; primary_worker_id?: number | null; - management_kind?: "backend_managed" | "runtime_unmanaged" | string | null; cleanup_target: { kind: string; working_directory_id: string;