From 7a32c13bd5450578abe48274a5218766a4faff92 Mon Sep 17 00:00:00 2001 From: "claude@clouddev1" Date: Mon, 11 May 2026 20:57:09 +0000 Subject: [PATCH] ADR-0022 stage 8d: schema cache refresh wiring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New `AppEvent::SchemaCacheRefreshed(SchemaCache)` event + App handler that stores it on `app.schema_cache`. Runtime helper `refresh_schema_cache(database, event_tx)` fetches table / column / relationship names via the `list_names_for` worker request (added in stage 7) and posts the assembled cache. Wired into every site that already posts `TablesRefreshed`: - `seed_initial_tables` (initial project load). - Project-switch path in `handle_project_switch`. - `RebuildSucceeded` path. - Post-DDL path (`spawn_command`). - Post-replay path. Result: schema-aware identifier completion (added in 8c) becomes live — Tab on `show data ` offers the actual table names from the current project, `drop column from T: ` (or similar) offers existing columns, etc. The cache stays fresh across DDL and rebuild without per-keystroke worker round-trips (one refresh per schema-mutating action is amortised across many subsequent keystrokes). Best-effort: a failed `list_names_for` for any individual slot kind leaves that field empty in the cache rather than suppressing the whole refresh — partial completion beats no completion. Tests: 738 passing, 0 failing, 1 ignored (unchanged total — this stage is wiring, not new test surface; the synthetic-cache tests from stage 8c remain the regression net for the completion logic itself). Clippy clean. --- src/app.rs | 10 ++++++++++ src/event.rs | 5 +++++ src/runtime.rs | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/src/app.rs b/src/app.rs index 14fb97f..6e682f2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -356,6 +356,16 @@ impl App { self.tables = tables; Vec::new() } + AppEvent::SchemaCacheRefreshed(cache) => { + trace!( + tables = cache.tables.len(), + columns = cache.columns.len(), + relationships = cache.relationships.len(), + "schema cache refreshed", + ); + self.schema_cache = cache; + Vec::new() + } AppEvent::PersistenceFatal { operation, path, diff --git a/src/event.rs b/src/event.rs index 03fafce..1be338c 100644 --- a/src/event.rs +++ b/src/event.rs @@ -69,6 +69,11 @@ pub enum AppEvent { }, /// Refreshed list of tables in the database. TablesRefreshed(Vec), + /// Refreshed schema lookup cache feeding Tab completion + /// for identifier slots (ADR-0022 §9 + stage 8d). Runtime + /// posts this alongside `TablesRefreshed` after project + /// load and after every successful DDL. + SchemaCacheRefreshed(crate::completion::SchemaCache), /// A persistence failure occurred (ADR-0015 §8). The /// application surfaces a fatal banner and exits cleanly so /// the message remains above the shell prompt. diff --git a/src/runtime.rs b/src/runtime.rs index ecedfd6..7e0bc85 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -466,6 +466,7 @@ async fn handle_project_switch( if let Ok(tables) = session.database().list_tables().await { let _ = event_tx.send(AppEvent::TablesRefreshed(tables)).await; } + refresh_schema_cache(session.database(), event_tx).await; } Err(e) => { let _ = event_tx @@ -817,6 +818,36 @@ async fn seed_initial_tables(database: &Database, event_tx: &mpsc::Sender, +) { + use crate::completion::SchemaCache; + use crate::dsl::ident_slot::IdentSlot; + let mut cache = SchemaCache::default(); + if let Ok(tables) = database.list_names_for(IdentSlot::TableName).await { + cache.tables = tables; + } + if let Ok(columns) = database.list_names_for(IdentSlot::Column).await { + cache.columns = columns; + } + if let Ok(rels) = database + .list_names_for(IdentSlot::RelationshipName) + .await + { + cache.relationships = rels; + } + let _ = event_tx.send(AppEvent::SchemaCacheRefreshed(cache)).await; } /// Read `project.yaml` + `data/` to compute the rebuild @@ -905,6 +936,7 @@ fn spawn_rebuild( if let Ok(tables) = database.list_tables().await { let _ = event_tx.send(AppEvent::TablesRefreshed(tables)).await; } + refresh_schema_cache(&database, &event_tx).await; } Err(DbError::PersistenceFatal { operation, @@ -1003,6 +1035,9 @@ fn spawn_dsl_dispatch( } Err(e) => warn!(error = %e, "post-list_tables failed"), } + // Refresh the schema cache feeding Tab completion + // (ADR-0022 §9). Same timing as TablesRefreshed. + refresh_schema_cache(&database, &event_tx).await; }); } @@ -1337,6 +1372,7 @@ fn spawn_replay( } Err(e) => warn!(error = %e, "post-replay list_tables failed"), } + refresh_schema_cache(&database, &event_tx).await; }); }