P0: workspace scaffold, CI gates, tc-domain, tc-config, tc-db vs real Postgres
- Cargo workspace with 1250-line and no-placeholder CI gates wired first - tc-domain: id newtypes, SessionKey codec (proptest round-trip), Role, GatedCategory (spec §15), AccessPolicy, core entities - tc-config: figment TOML+env config, DeployTarget/provider/auth selection with semantic validation - migrations/0001: full spec §14 schema incl. DB-enforced append-only audit_log - tc-db: compile-time-checked sqlx repos (workspaces, users, agents+policies, credits, audit) with committed .sqlx offline metadata - tc-testkit: per-test real-Postgres databases (testcontainers or TC_TEST_DATABASE_URL), embedded migrations Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
//! Postgres persistence for TeamClaw.
|
||||
//!
|
||||
//! Queries are compile-time checked (`sqlx::query!`) against the schema in
|
||||
//! `/migrations`, and every repository is tested only against a real
|
||||
//! Postgres via `tc-testkit` — no in-memory store exists.
|
||||
|
||||
pub mod repo;
|
||||
|
||||
use sqlx::postgres::PgPoolOptions;
|
||||
use sqlx::PgPool;
|
||||
|
||||
/// All schema migrations, embedded so binaries can self-migrate at boot.
|
||||
pub static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("../../migrations");
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum DbError {
|
||||
#[error("not found")]
|
||||
NotFound,
|
||||
#[error("conflict: {0}")]
|
||||
Conflict(String),
|
||||
#[error(transparent)]
|
||||
Other(sqlx::Error),
|
||||
}
|
||||
|
||||
impl From<sqlx::Error> for DbError {
|
||||
fn from(err: sqlx::Error) -> Self {
|
||||
match &err {
|
||||
sqlx::Error::RowNotFound => DbError::NotFound,
|
||||
sqlx::Error::Database(db) if db.is_unique_violation() => {
|
||||
DbError::Conflict(db.message().to_owned())
|
||||
}
|
||||
_ => DbError::Other(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Connects a pool sized from configuration. Callers run `MIGRATOR` at boot.
|
||||
pub async fn connect(url: &str, max_connections: u32) -> Result<PgPool, DbError> {
|
||||
Ok(PgPoolOptions::new()
|
||||
.max_connections(max_connections)
|
||||
.connect(url)
|
||||
.await?)
|
||||
}
|
||||
Reference in New Issue
Block a user