commit c0b5c2deef93095613f50e27fcbb53a26db61b2b
parent fdf71dacc29d186713482f1437e30ebb58b40b6d
Author: triesap <tyson@radroots.org>
Date: Tue, 30 Jun 2026 10:13:25 +0000
store: enforce current schema cleanup
Diffstat:
7 files changed, 100 insertions(+), 93 deletions(-)
diff --git a/crates/desktop/src/source_guards.rs b/crates/desktop/src/source_guards.rs
@@ -1517,6 +1517,60 @@ fn app_production_sdk_boundary_usage_is_exception_scoped() {
}
#[test]
+fn app_store_current_schema_surfaces_reject_retired_terms() {
+ let app_root = app_root();
+ let mut paths = vec![
+ app_root.join("crates/store/src/lib.rs"),
+ app_root.join("crates/sync/src/publish.rs"),
+ ];
+ for entry in
+ fs::read_dir(app_root.join("crates/store/migrations")).expect("read store migrations")
+ {
+ let path = entry.expect("migration entry").path();
+ if path.extension().and_then(|ext| ext.to_str()) == Some("sql") {
+ paths.push(path);
+ }
+ }
+
+ let offenders = paths
+ .iter()
+ .flat_map(|path| {
+ let source = read_source_path(path);
+ let relative_path = path
+ .strip_prefix(app_root.as_path())
+ .expect("app relative path")
+ .display()
+ .to_string();
+ APP_STORE_RETIRED_SCHEMA_TERMS
+ .iter()
+ .filter(move |term| source.contains(**term))
+ .map(move |term| (relative_path.clone(), *term))
+ })
+ .collect::<Vec<_>>();
+
+ assert!(
+ offenders.is_empty(),
+ "app store current-schema surfaces retain retired terms: {offenders:?}"
+ );
+}
+
+const APP_STORE_RETIRED_SCHEMA_TERMS: &[&str] = &[
+ "legacy",
+ "compat",
+ "shim",
+ "deprecated",
+ "dual_read",
+ "dual_write",
+ "dual-read",
+ "dual-write",
+ "AppSdkMigration",
+ "sdk_migration",
+ "migration_receipt",
+ "migration_audit",
+ "migration_scaffold",
+];
+
+#[test]
fn strict_sdk_boundary_scanner_rejects_unexcepted_new_production_paths() {
let findings = unexcepted_sdk_boundary_patterns(
"crates/desktop/src/new_workflow.rs",
diff --git a/crates/store/migrations/0010_sync_contract_alignment.sql b/crates/store/migrations/0010_sync_contract_alignment.sql
@@ -1,5 +1,5 @@
-ALTER TABLE local_outbox RENAME TO local_outbox_legacy;
-ALTER TABLE local_conflicts RENAME TO local_conflicts_legacy;
+ALTER TABLE local_outbox RENAME TO local_outbox_rebuild_source;
+ALTER TABLE local_conflicts RENAME TO local_conflicts_rebuild_source;
DROP TABLE sync_checkpoints;
CREATE TABLE local_outbox (
@@ -56,8 +56,8 @@ CREATE TABLE sync_checkpoints (
last_error_message TEXT
);
-DROP TABLE local_outbox_legacy;
-DROP TABLE local_conflicts_legacy;
+DROP TABLE local_outbox_rebuild_source;
+DROP TABLE local_conflicts_rebuild_source;
CREATE INDEX idx_local_outbox_account_available_at ON local_outbox(
account_id,
diff --git a/crates/store/migrations/0016_deterministic_outbox.sql b/crates/store/migrations/0016_deterministic_outbox.sql
@@ -1,4 +1,4 @@
-ALTER TABLE local_outbox RENAME TO local_outbox_legacy;
+ALTER TABLE local_outbox RENAME TO local_outbox_rebuild_source;
CREATE TABLE local_outbox (
id TEXT PRIMARY KEY NOT NULL,
@@ -59,10 +59,10 @@ SELECT
attempt_count,
'pending',
NULL
-FROM local_outbox_legacy
+FROM local_outbox_rebuild_source
ORDER BY available_at ASC, created_at ASC, id ASC;
-DROP TABLE local_outbox_legacy;
+DROP TABLE local_outbox_rebuild_source;
CREATE INDEX idx_local_outbox_account_available_at ON local_outbox(
account_id,
diff --git a/crates/store/migrations/0020_declined_order_status.sql b/crates/store/migrations/0020_declined_order_status.sql
@@ -5,9 +5,9 @@ DROP INDEX IF EXISTS idx_orders_farm_status;
DROP INDEX IF EXISTS idx_orders_farm_window_status_updated_at;
DROP INDEX IF EXISTS idx_orders_buyer_context_updated_at;
-ALTER TABLE order_lines RENAME TO order_lines_legacy;
-ALTER TABLE buyer_order_coordination_records RENAME TO buyer_order_coordination_records_legacy;
-ALTER TABLE orders RENAME TO orders_legacy;
+ALTER TABLE order_lines RENAME TO order_lines_rebuild_source;
+ALTER TABLE buyer_order_coordination_records RENAME TO buyer_order_coordination_records_rebuild_source;
+ALTER TABLE orders RENAME TO orders_rebuild_source;
CREATE TABLE orders (
id TEXT PRIMARY KEY NOT NULL,
@@ -50,7 +50,7 @@ SELECT
buyer_email,
buyer_phone,
buyer_order_note
-FROM orders_legacy;
+FROM orders_rebuild_source;
CREATE TABLE order_lines (
id TEXT PRIMARY KEY NOT NULL,
@@ -105,7 +105,7 @@ SELECT
listing_event_id,
seller_pubkey,
listing_relays_json
-FROM order_lines_legacy;
+FROM order_lines_rebuild_source;
CREATE TABLE buyer_order_coordination_records (
order_id TEXT PRIMARY KEY NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
@@ -143,7 +143,7 @@ SELECT
created_at,
updated_at,
synced_at
-FROM buyer_order_coordination_records_legacy;
+FROM buyer_order_coordination_records_rebuild_source;
CREATE INDEX idx_orders_farm_status ON orders(farm_id, status);
CREATE INDEX idx_orders_farm_window_status_updated_at
@@ -158,6 +158,6 @@ CREATE INDEX idx_buyer_order_coordination_context_state_updated_at
CREATE INDEX idx_buyer_order_coordination_state_updated_at
ON buyer_order_coordination_records(state, updated_at);
-DROP TABLE order_lines_legacy;
-DROP TABLE buyer_order_coordination_records_legacy;
-DROP TABLE orders_legacy;
+DROP TABLE order_lines_rebuild_source;
+DROP TABLE buyer_order_coordination_records_rebuild_source;
+DROP TABLE orders_rebuild_source;
diff --git a/crates/store/migrations/0027_local_interop_validation_receipt_projection_kind.sql b/crates/store/migrations/0027_local_interop_validation_receipt_projection_kind.sql
@@ -2,7 +2,7 @@ DROP INDEX IF EXISTS idx_local_interop_imports_seq;
DROP INDEX IF EXISTS idx_local_interop_imports_owner_status;
DROP INDEX IF EXISTS idx_local_interop_imports_projected;
-ALTER TABLE local_interop_imports RENAME TO local_interop_imports_validation_receipt_projection_kind_legacy;
+ALTER TABLE local_interop_imports RENAME TO local_interop_imports_validation_receipt_projection_kind_rebuild_source;
CREATE TABLE local_interop_imports (
record_id TEXT PRIMARY KEY NOT NULL,
@@ -89,7 +89,7 @@ SELECT
event_content,
event_sig,
raw_event_json
-FROM local_interop_imports_validation_receipt_projection_kind_legacy;
+FROM local_interop_imports_validation_receipt_projection_kind_rebuild_source;
CREATE INDEX idx_local_interop_imports_seq
ON local_interop_imports(local_seq);
@@ -100,4 +100,4 @@ CREATE INDEX idx_local_interop_imports_owner_status
CREATE INDEX idx_local_interop_imports_projected
ON local_interop_imports(projected_kind, projected_id);
-DROP TABLE local_interop_imports_validation_receipt_projection_kind_legacy;
+DROP TABLE local_interop_imports_validation_receipt_projection_kind_rebuild_source;
diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs
@@ -789,7 +789,7 @@ fn apply_migrations(connection: &mut Connection) -> Result<(), AppSqliteError> {
#[cfg(test)]
mod tests {
- use super::{AppSqliteStore, DatabaseTarget, latest_schema_version, migrations};
+ use super::{AppSqliteStore, DatabaseTarget, latest_schema_version};
use rusqlite::{Connection, params};
use std::{
env, fs,
@@ -1190,27 +1190,8 @@ mod tests {
}
#[test]
- fn legacy_sync_scaffolding_migrates_to_account_scoped_contract() {
- let path = temp_database_path("legacy-sync-contract");
- fs::create_dir_all(path.parent().expect("temp database should have a parent"))
- .expect("legacy database parent should exist");
- let connection = Connection::open(&path).expect("legacy database should open");
-
- for (version, sql) in migrations::pending_migrations(0)
- .filter(|(version, _)| *version < latest_schema_version())
- {
- connection
- .execute_batch(sql)
- .expect("legacy migration should apply");
- connection
- .pragma_update(None, "user_version", version)
- .expect("legacy schema version should record");
- }
-
- drop(connection);
-
- let store =
- AppSqliteStore::open(DatabaseTarget::Path(path.clone())).expect("store should open");
+ fn sync_contract_schema_bootstraps_account_scoped_tables() {
+ let store = AppSqliteStore::open(DatabaseTarget::InMemory).expect("store should open");
let connection = store.connection();
assert_eq!(
@@ -1229,38 +1210,19 @@ mod tests {
assert!(column_exists(connection, "sync_checkpoints", "state"));
assert!(table_exists(connection, "app_relay_ingest_freshness"));
assert_eq!(row_count(connection, "sync_checkpoints"), 0);
-
- drop(store);
- remove_database_artifacts(&path);
}
#[test]
- fn legacy_orders_status_migration_preserves_child_rows_and_accepts_declined() {
- let path = temp_database_path("legacy-declined-orders");
- fs::create_dir_all(path.parent().expect("temp database should have a parent"))
- .expect("legacy database parent should exist");
- let connection = Connection::open(&path).expect("legacy database should open");
- connection
- .execute_batch("PRAGMA foreign_keys = ON")
- .expect("foreign keys should enable");
-
- for (version, sql) in migrations::pending_migrations(0).filter(|(version, _)| *version < 20)
- {
- connection
- .execute_batch(sql)
- .expect("legacy migration should apply");
- connection
- .pragma_update(None, "user_version", version)
- .expect("legacy schema version should record");
- }
-
+ fn order_status_schema_preserves_child_rows_and_accepts_declined() {
+ let store = AppSqliteStore::open(DatabaseTarget::InMemory).expect("store should open");
+ let connection = store.connection();
connection
.execute(
"INSERT INTO farms (id, display_name, readiness, created_at, updated_at)
- VALUES (?1, 'Legacy Farm', 'ready', '2026-01-01T00:00:00Z', '2026-01-01T00:00:00Z')",
- params!["farm_legacy"],
+ VALUES (?1, 'Schema Farm', 'ready', '2026-01-01T00:00:00Z', '2026-01-01T00:00:00Z')",
+ params!["farm_status"],
)
- .expect("legacy farm should insert");
+ .expect("farm should insert");
connection
.execute(
"INSERT INTO orders (
@@ -1276,11 +1238,11 @@ mod tests {
buyer_phone,
buyer_order_note
) VALUES (
- 'order_legacy',
- 'farm_legacy',
+ 'order_status',
+ 'farm_status',
NULL,
'R-900',
- 'Legacy Buyer',
+ 'Schema Buyer',
'needs_action',
'2026-01-01T00:00:00Z',
'account:buyer',
@@ -1290,7 +1252,7 @@ mod tests {
)",
[],
)
- .expect("legacy order should insert");
+ .expect("order should insert");
connection
.execute(
"INSERT INTO order_lines (
@@ -1300,15 +1262,15 @@ mod tests {
quantity_value,
quantity_display
) VALUES (
- 'line_legacy',
- 'order_legacy',
- 'Legacy Eggs',
+ 'line_status',
+ 'order_status',
+ 'Schema Eggs',
2,
'2 each'
)",
[],
)
- .expect("legacy order line should insert");
+ .expect("order line should insert");
connection
.execute(
"INSERT INTO buyer_order_coordination_records (
@@ -1318,7 +1280,7 @@ mod tests {
created_at,
updated_at
) VALUES (
- 'order_legacy',
+ 'order_status',
'account:buyer',
'pending',
'2026-01-01T00:00:00Z',
@@ -1326,13 +1288,7 @@ mod tests {
)",
[],
)
- .expect("legacy buyer coordination should insert");
-
- drop(connection);
-
- let store =
- AppSqliteStore::open(DatabaseTarget::Path(path.clone())).expect("store should open");
- let connection = store.connection();
+ .expect("buyer coordination should insert");
assert_eq!(
store.schema_version().expect("schema version"),
@@ -1345,20 +1301,20 @@ mod tests {
connection
.execute(
- "UPDATE orders SET status = 'declined' WHERE id = 'order_legacy'",
+ "UPDATE orders SET status = 'declined' WHERE id = 'order_status'",
[],
)
- .expect("declined status should satisfy migrated check");
+ .expect("declined status should satisfy current check");
connection
.execute(
- "UPDATE orders SET status = 'needs_review' WHERE id = 'order_legacy'",
+ "UPDATE orders SET status = 'needs_review' WHERE id = 'order_status'",
[],
)
- .expect("needs review status should satisfy migrated check");
+ .expect("needs review status should satisfy current check");
let status: String = connection
.query_row(
- "SELECT status FROM orders WHERE id = 'order_legacy'",
+ "SELECT status FROM orders WHERE id = 'order_status'",
[],
|row| row.get(0),
)
@@ -1366,13 +1322,10 @@ mod tests {
assert_eq!(status, "needs_review");
connection
.execute(
- "UPDATE orders SET workflow_agreement = 'agreed_pending_rhi' WHERE id = 'order_legacy'",
+ "UPDATE orders SET workflow_agreement = 'agreed_pending_rhi' WHERE id = 'order_status'",
[],
)
- .expect("agreed pending rhi agreement should satisfy migrated check");
-
- drop(store);
- remove_database_artifacts(&path);
+ .expect("agreed pending rhi agreement should satisfy current check");
}
fn table_exists(connection: &Connection, table_name: &str) -> bool {
diff --git a/crates/sync/src/publish.rs b/crates/sync/src/publish.rs
@@ -1051,7 +1051,7 @@ mod tests {
}
#[test]
- fn existing_raw_payload_outbox_work_remains_local_save_compatible() {
+ fn existing_raw_payload_outbox_work_rejects_publish_payload() {
let pending_operation = PendingSyncOperation {
operation_key: "product:greens:upsert".to_owned(),
aggregate: SyncAggregateRef::Product(ProductId::new()),