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})" )