commit 540e3195cf280da694baa340512769a13541b635
parent da49136dc0f2b34720ffc3db349b8e63e2f62fd5
Author: triesap <tyson@radroots.org>
Date: Sun, 1 Mar 2026 16:16:20 +0000
refactor: harden replica naming and legacy identifier guard
Diffstat:
12 files changed, 68 insertions(+), 48 deletions(-)
diff --git a/.github/workflows/sdk-contract-ci.yml b/.github/workflows/sdk-contract-ci.yml
@@ -16,6 +16,9 @@ jobs:
- name: guard committed ts artifacts
run: ./scripts/ci/guard_committed_ts_artifacts.sh
+ - name: guard legacy identifiers
+ run: ./scripts/ci/guard_no_legacy_identifiers.sh
+
- name: install rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
diff --git a/contract/manifest.toml b/contract/manifest.toml
@@ -41,6 +41,6 @@ require_reproducible_exports = true
require_conformance_vectors = true
[policy.replica]
-forbid_legacy_tangle_identifiers = true
+forbid_legacy_alias_identifiers = true
require_transport_agnostic_sync_contract = true
require_deterministic_emit_ingest = true
diff --git a/contract/replica.toml b/contract/replica.toml
@@ -14,4 +14,4 @@ sync_wasm = "radroots-replica-sync-wasm"
transport_agnostic_sync_core = true
deterministic_emit_and_ingest = true
wasm_exports_prefix = "replica_"
-forbid_legacy_tangle_identifiers = true
+forbid_legacy_alias_identifiers = true
diff --git a/crates/replica-db-wasm/src/wasm_impl.rs b/crates/replica-db-wasm/src/wasm_impl.rs
@@ -3,7 +3,7 @@ use radroots_sql_core::{
};
use radroots_sql_wasm_core::{err_js, parse_json};
use radroots_replica_db::migrations;
-use radroots_replica_db::{TangleDbExportManifestRs, export_manifest};
+use radroots_replica_db::{ReplicaDbExportManifestRs, export_manifest};
use radroots_replica_sync::radroots_replica_sync_status;
use wasm_bindgen::JsValue;
use wasm_bindgen::prelude::*;
@@ -141,7 +141,7 @@ fn export_snapshot(exec: &WasmSqlExecutor) -> Result<JsValue, JsValue> {
if status.pending_count > 0 {
return Err(err_js(radroots_sql_core::SqlError::InvalidArgument(
format!(
- "tangle db export requires synced state (pending {}/{})",
+ "replica db export requires synced state (pending {}/{})",
status.pending_count, status.expected_count
),
)));
@@ -150,13 +150,13 @@ fn export_snapshot(exec: &WasmSqlExecutor) -> Result<JsValue, JsValue> {
export_snapshot_value(manifest)
}
-fn export_snapshot_value(manifest: TangleDbExportManifestRs) -> Result<JsValue, JsValue> {
+fn export_snapshot_value(manifest: ReplicaDbExportManifestRs) -> Result<JsValue, JsValue> {
let bytes_js = radroots_sql_wasm_core::export_bytes();
export_snapshot_value_with_bytes(manifest, bytes_js)
}
fn export_snapshot_value_with_bytes(
- manifest: TangleDbExportManifestRs,
+ manifest: ReplicaDbExportManifestRs,
bytes_js: JsValue,
) -> Result<JsValue, JsValue> {
let manifest_js = serde_wasm_bindgen::to_value(&manifest).map_err(|err| {
@@ -180,7 +180,7 @@ mod tests {
#[wasm_bindgen_test::wasm_bindgen_test]
fn export_snapshot_value_includes_fields() {
- let manifest = radroots_replica_db::TangleDbExportManifestRs {
+ let manifest = radroots_replica_db::ReplicaDbExportManifestRs {
export_version: "1".to_string(),
replica_db_version: "0.0.0".to_string(),
backup_format_version: "0.0.0".to_string(),
diff --git a/crates/replica-db/src/backup.rs b/crates/replica-db/src/backup.rs
@@ -4,7 +4,7 @@ use serde_json::{Map, Value};
use std::collections::{BTreeMap, HashMap};
pub const DATABASE_BACKUP_VERSION: &str = "1.0.0";
-pub const TANGLE_DB_VERSION: &str = env!("CARGO_PKG_VERSION");
+pub const REPLICA_DB_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaEntry {
@@ -44,7 +44,7 @@ pub fn export_database_backup<E: SqlExecutor>(executor: &E) -> Result<DatabaseBa
let migrations = export_migrations();
Ok(DatabaseBackup {
format_version: DATABASE_BACKUP_VERSION.to_string(),
- replica_db_version: TANGLE_DB_VERSION.to_string(),
+ replica_db_version: REPLICA_DB_VERSION.to_string(),
schema,
migrations,
data,
@@ -277,10 +277,10 @@ fn validate_backup_version(backup: &DatabaseBackup) -> Result<(), SqlError> {
backup.format_version, DATABASE_BACKUP_VERSION
)));
}
- if backup.replica_db_version != TANGLE_DB_VERSION {
+ if backup.replica_db_version != REPLICA_DB_VERSION {
return Err(SqlError::InvalidArgument(format!(
"unsupported replica-db version {}, expected {}",
- backup.replica_db_version, TANGLE_DB_VERSION
+ backup.replica_db_version, REPLICA_DB_VERSION
)));
}
Ok(())
@@ -394,7 +394,7 @@ mod tests {
);
let backup = DatabaseBackup {
format_version: DATABASE_BACKUP_VERSION.to_string(),
- replica_db_version: TANGLE_DB_VERSION.to_string(),
+ replica_db_version: REPLICA_DB_VERSION.to_string(),
schema: vec![SchemaEntry {
object_type: String::from("table"),
name: String::from("fail_table"),
@@ -517,7 +517,7 @@ mod tests {
row.insert(String::from("co\"l"), Value::from(7));
let backup = DatabaseBackup {
format_version: DATABASE_BACKUP_VERSION.to_string(),
- replica_db_version: TANGLE_DB_VERSION.to_string(),
+ replica_db_version: REPLICA_DB_VERSION.to_string(),
schema: vec![
SchemaEntry {
object_type: String::from("table"),
@@ -581,7 +581,7 @@ mod tests {
#[test]
fn validate_backup_version_rejects_invalid_versions() {
- let wrong_format = backup_with_versions("0.0.1", TANGLE_DB_VERSION);
+ let wrong_format = backup_with_versions("0.0.1", REPLICA_DB_VERSION);
let err = validate_backup_version(&wrong_format).expect_err("format version must fail");
assert!(matches!(err, SqlError::InvalidArgument(_)));
@@ -599,7 +599,7 @@ mod tests {
)],
None,
);
- let backup = backup_with_versions(DATABASE_BACKUP_VERSION, TANGLE_DB_VERSION);
+ let backup = backup_with_versions(DATABASE_BACKUP_VERSION, REPLICA_DB_VERSION);
let matched = executor
.query_raw("select type, name from sqlite_master", "[]")
diff --git a/crates/replica-db/src/export.rs b/crates/replica-db/src/export.rs
@@ -3,11 +3,11 @@ use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use crate::backup::{
- DATABASE_BACKUP_VERSION, MigrationBackup, SchemaEntry, TANGLE_DB_VERSION, escape_identifier,
+ DATABASE_BACKUP_VERSION, MigrationBackup, SchemaEntry, REPLICA_DB_VERSION, escape_identifier,
export_migrations, load_schema,
};
-pub const TANGLE_DB_EXPORT_VERSION: &str = "1";
+pub const REPLICA_DB_EXPORT_VERSION: &str = "1";
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableCount {
@@ -16,7 +16,7 @@ pub struct TableCount {
}
#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct TangleDbExportManifestRs {
+pub struct ReplicaDbExportManifestRs {
pub export_version: String,
pub replica_db_version: String,
pub backup_format_version: String,
@@ -26,14 +26,14 @@ pub struct TangleDbExportManifestRs {
pub table_counts: Vec<TableCount>,
}
-pub fn export_manifest<E: SqlExecutor>(executor: &E) -> Result<TangleDbExportManifestRs, SqlError> {
+pub fn export_manifest<E: SqlExecutor>(executor: &E) -> Result<ReplicaDbExportManifestRs, SqlError> {
let schema = load_schema(executor)?;
let migrations = export_migrations();
let table_counts = load_table_counts(executor, &schema)?;
let schema_hash = schema_hash(&schema)?;
- Ok(TangleDbExportManifestRs {
- export_version: TANGLE_DB_EXPORT_VERSION.to_string(),
- replica_db_version: TANGLE_DB_VERSION.to_string(),
+ Ok(ReplicaDbExportManifestRs {
+ export_version: REPLICA_DB_EXPORT_VERSION.to_string(),
+ replica_db_version: REPLICA_DB_VERSION.to_string(),
backup_format_version: DATABASE_BACKUP_VERSION.to_string(),
schema_hash,
schema,
diff --git a/crates/replica-db/src/lib.rs b/crates/replica-db/src/lib.rs
@@ -121,22 +121,22 @@ pub mod models;
#[cfg(not(feature = "coverage-minimal"))]
pub use backup::{DatabaseBackup, MigrationBackup, SchemaEntry};
#[cfg(not(feature = "coverage-minimal"))]
-pub use export::{TANGLE_DB_EXPORT_VERSION, TableCount, TangleDbExportManifestRs, export_manifest};
+pub use export::{REPLICA_DB_EXPORT_VERSION, TableCount, ReplicaDbExportManifestRs, export_manifest};
#[cfg(not(feature = "coverage-minimal"))]
pub use models::*;
-pub struct TangleSql<E: SqlExecutor> {
+pub struct ReplicaSql<E: SqlExecutor> {
executor: E,
}
-impl<E: SqlExecutor> TangleSql<E> {
+impl<E: SqlExecutor> ReplicaSql<E> {
pub fn coverage_branch_probe(enabled: bool) -> &'static str {
if enabled { "enabled" } else { "disabled" }
}
}
#[cfg(not(feature = "coverage-minimal"))]
-impl<E: SqlExecutor> TangleSql<E> {
+impl<E: SqlExecutor> ReplicaSql<E> {
pub fn new(executor: E) -> Self {
Self { executor }
}
@@ -720,7 +720,7 @@ impl<E: SqlExecutor> TangleSql<E> {
}
#[cfg(feature = "coverage-minimal")]
-impl<E: SqlExecutor> TangleSql<E> {
+impl<E: SqlExecutor> ReplicaSql<E> {
pub fn new(executor: E) -> Self {
Self { executor }
}
@@ -732,7 +732,7 @@ impl<E: SqlExecutor> TangleSql<E> {
#[cfg(test)]
mod tests {
- use super::TangleSql;
+ use super::ReplicaSql;
use radroots_sql_core::{ExecOutcome, SqlError, SqlExecutor};
struct ProbeExecutor;
@@ -763,8 +763,8 @@ mod tests {
}
#[test]
- fn tangle_sql_constructor_and_executor_access_are_supported() {
- let db = TangleSql::new(ProbeExecutor);
+ fn replica_sql_constructor_and_executor_access_are_supported() {
+ let db = ReplicaSql::new(ProbeExecutor);
let exec = db.executor();
assert!(exec.exec("select 1", "[]").is_ok());
assert!(exec.query_raw("select 1", "[]").is_ok());
@@ -772,11 +772,11 @@ mod tests {
assert!(exec.commit().is_ok());
assert!(exec.rollback().is_ok());
assert_eq!(
- TangleSql::<ProbeExecutor>::coverage_branch_probe(true),
+ ReplicaSql::<ProbeExecutor>::coverage_branch_probe(true),
"enabled"
);
assert_eq!(
- TangleSql::<ProbeExecutor>::coverage_branch_probe(false),
+ ReplicaSql::<ProbeExecutor>::coverage_branch_probe(false),
"disabled"
);
}
diff --git a/crates/replica-db/tests/full_mode.rs b/crates/replica-db/tests/full_mode.rs
@@ -1,5 +1,5 @@
use radroots_sql_core::{SqlError, SqliteExecutor};
-use radroots_replica_db::{TangleSql, export_manifest};
+use radroots_replica_db::{ReplicaSql, export_manifest};
use radroots_replica_db_schema::farm::{
IFarmCreate, IFarmDelete, IFarmFindMany, IFarmFindOne, IFarmUpdate,
};
@@ -87,9 +87,9 @@ fn assert_not_found<T>(result: Result<T, IError<SqlError>>) {
assert!(matches!(err.err, SqlError::NotFound(_)));
}
-fn open_db() -> TangleSql<SqliteExecutor> {
+fn open_db() -> ReplicaSql<SqliteExecutor> {
let exec = SqliteExecutor::open_memory().expect("open sqlite memory");
- let db = TangleSql::new(exec);
+ let db = ReplicaSql::new(exec);
db.migrate_up().expect("migrate up");
db
}
diff --git a/crates/replica-sync/src/error.rs b/crates/replica-sync/src/error.rs
@@ -19,11 +19,11 @@ pub enum RadrootsReplicaEventsError {
impl fmt::Display for RadrootsReplicaEventsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- Self::Sql(err) => write!(f, "tangle_events.sql: {}", err.err.to_string()),
- Self::Encode(err) => write!(f, "tangle_events.encode: {err}"),
- Self::Parse(err) => write!(f, "tangle_events.parse: {err}"),
- Self::InvalidSelector(msg) => write!(f, "tangle_events.selector: {msg}"),
- Self::InvalidData(msg) => write!(f, "tangle_events.data: {msg}"),
+ Self::Sql(err) => write!(f, "replica_sync.sql: {}", err.err.to_string()),
+ Self::Encode(err) => write!(f, "replica_sync.encode: {err}"),
+ Self::Parse(err) => write!(f, "replica_sync.parse: {err}"),
+ Self::InvalidSelector(msg) => write!(f, "replica_sync.selector: {msg}"),
+ Self::InvalidData(msg) => write!(f, "replica_sync.data: {msg}"),
}
}
}
@@ -59,20 +59,20 @@ mod tests {
#[test]
fn display_formats_all_error_variants() {
let sql_err = RadrootsReplicaEventsError::Sql(IError::from(SqlError::Internal));
- assert!(sql_err.to_string().contains("tangle_events.sql"));
+ assert!(sql_err.to_string().contains("replica_sync.sql"));
let encode_err = RadrootsReplicaEventsError::Encode(EventEncodeError::InvalidField("name"));
- assert!(encode_err.to_string().contains("tangle_events.encode"));
+ assert!(encode_err.to_string().contains("replica_sync.encode"));
let parse_err = RadrootsReplicaEventsError::Parse(EventParseError::InvalidTag("d"));
- assert!(parse_err.to_string().contains("tangle_events.parse"));
+ assert!(parse_err.to_string().contains("replica_sync.parse"));
let selector_err =
RadrootsReplicaEventsError::InvalidSelector("selector missing".to_string());
- assert!(selector_err.to_string().contains("tangle_events.selector"));
+ assert!(selector_err.to_string().contains("replica_sync.selector"));
let data_err = RadrootsReplicaEventsError::InvalidData("bad data".to_string());
- assert!(data_err.to_string().contains("tangle_events.data"));
+ assert!(data_err.to_string().contains("replica_sync.data"));
}
#[test]
diff --git a/crates/replica-sync/src/ingest.rs b/crates/replica-sync/src/ingest.rs
@@ -1948,10 +1948,10 @@ mod tests {
fn create_gcs_location_error_mapping_helpers_are_covered() {
let point_json_err = serde_json::from_str::<Value>("{").expect_err("invalid json");
let point_err = map_gcs_point_serialize_error(point_json_err);
- assert_eq!(point_err.to_string(), "tangle_events.data: gcs.point");
+ assert_eq!(point_err.to_string(), "replica_sync.data: gcs.point");
let polygon_json_err = serde_json::from_str::<Value>("{").expect_err("invalid json");
let polygon_err = map_gcs_polygon_serialize_error(polygon_json_err);
- assert_eq!(polygon_err.to_string(), "tangle_events.data: gcs.polygon");
+ assert_eq!(polygon_err.to_string(), "replica_sync.data: gcs.polygon");
}
}
diff --git a/crates/sql-core/src/export_lock.rs b/crates/sql-core/src/export_lock.rs
@@ -5,7 +5,7 @@ use crate::error::SqlError;
use std::cell::Cell;
use std::sync::atomic::{AtomicBool, Ordering};
-pub(crate) const EXPORT_LOCK_ERR: &str = "tangle db export in progress";
+pub(crate) const EXPORT_LOCK_ERR: &str = "replica db export in progress";
static EXPORT_LOCK_ACTIVE: AtomicBool = AtomicBool::new(false);
diff --git a/scripts/ci/guard_no_legacy_identifiers.sh b/scripts/ci/guard_no_legacy_identifiers.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+matches="$(
+ git grep -nI 'tangle' -- . \
+ ':(exclude)AGENTS.md' \
+ ':(exclude)scripts/ci/guard_no_legacy_identifiers.sh' \
+ || true
+)"
+
+if [[ -n "$matches" ]]; then
+ echo "legacy identifier 'tangle' is forbidden in tracked oss files"
+ echo "$matches"
+ exit 1
+fi
+
+echo "no legacy 'tangle' identifiers found in tracked oss files"