From 4cd5a7fec7585dac36bd77e265cd50ac7fd2fbb4 Mon Sep 17 00:00:00 2001 From: CleverThis Date: Fri, 15 May 2026 04:47:31 +0000 Subject: [PATCH] fix(invariants): align InvariantModel with codebase ULID and timestamp standards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit InvariantModel used String(36) UUID-style PK instead of the codebase- standard ULID (String(26)), and was missing the required updated_at column present on all peer entity models. Updated both the ORM class and the migration DDL to match conventions: - PK: String(36) → String(26) for ULID compatibility with resource_id, decision_id, checkpoint_id, job_id, etc. - Added updated_at column (String(30)) matching NamespacedProjectModel, ResourceLinkModel and other entity tables - Updated migration `m3_001_invariants_table` DDL to mirror ORM model - Added index on updated_at for audit-query performance --- .../migrations/versions/m3_001_invariants_table.py | 11 +++++++++-- src/cleveragents/infrastructure/database/models.py | 12 +++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/cleveragents/infrastructure/database/migrations/versions/m3_001_invariants_table.py b/src/cleveragents/infrastructure/database/migrations/versions/m3_001_invariants_table.py index 45c8cc800..a66b4e757 100644 --- a/src/cleveragents/infrastructure/database/migrations/versions/m3_001_invariants_table.py +++ b/src/cleveragents/infrastructure/database/migrations/versions/m3_001_invariants_table.py @@ -22,12 +22,17 @@ depends_on: str | Sequence[str] | None = None def upgrade() -> None: - """Create the invariants table with index on is_active.""" + """Create the invariants table with indexes on is_active and updated_at. + + The ``id`` column uses ULID (26-char string) for consistency with all + other models in the codebase (resource_id, decision_id, etc.). + """ op.create_table( "invariants", - sa.Column("id", sa.String(36), nullable=False), + sa.Column("id", sa.String(26), nullable=False), sa.Column("description", sa.Text, nullable=False), sa.Column("created_at", sa.String(30), nullable=False), + sa.Column("updated_at", sa.String(30), nullable=False), sa.Column( "is_active", sa.Boolean, @@ -38,9 +43,11 @@ def upgrade() -> None: sa.CheckConstraint("description != ''"), ) op.create_index("ix_invariants_is_active", "invariants", ["is_active"]) + op.create_index("ix_invariants_updated_at", "invariants", ["updated_at"]) def downgrade() -> None: """Drop the invariants table.""" + op.drop_index("ix_invariants_updated_at", table_name="invariants") op.drop_index("ix_invariants_is_active", table_name="invariants") op.drop_table("invariants") diff --git a/src/cleveragents/infrastructure/database/models.py b/src/cleveragents/infrastructure/database/models.py index 343fc4a3c..db9810fd0 100644 --- a/src/cleveragents/infrastructure/database/models.py +++ b/src/cleveragents/infrastructure/database/models.py @@ -1315,8 +1315,9 @@ class InvariantModel(Base): # type: ignore[misc] """SQLAlchemy database model for global invariants. Stores user-defined constraint rules that must hold true across all - planning sessions. Each invariant carries a UUID identifier, a human- - readable description, and an active flag for soft-deletion (is_active). + planning sessions. Each invariant carries a ULID identifier, a human- + readable description, an active flag for soft-deletion (is_active), and + audit timestamps. Mapped to table ``invariants`` (migration ``m3_001_invariants_table``). """ @@ -1324,9 +1325,12 @@ class InvariantModel(Base): # type: ignore[misc] __allow_unmapped__ = True __tablename__ = "invariants" - id = Column(String(36), primary_key=True) + # PK: ULID (26-char string) — consistent with all other models in the + # codebase (resource_id, decision_id, checkpoint_id, job_id, etc.). + id = Column(String(26), primary_key=True) description = Column(Text, nullable=False) created_at = Column(String(30), nullable=False) + updated_at = Column(String(30), nullable=False) is_active = Column( Boolean, nullable=False, @@ -1340,6 +1344,8 @@ class InvariantModel(Base): # type: ignore[misc] return ( f"InvariantModel(id={self.id!r}, " f"description={self.description!r}, " + f"created_at={self.created_at!r}, " + f"updated_at={self.updated_at!r}, " f"is_active={self.is_active})" )