fix(memory): add data integrity constraints to Fact model

- Change source_episode_ids from JSON to JSONB for PostgreSQL consistency
- Add unique constraint for global facts (project_id IS NULL)
- Add CHECK constraint ensuring reinforcement_count >= 1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-05 17:39:30 +01:00
parent 74b8c65741
commit 33ec889fc4

View File

@@ -18,9 +18,8 @@ from sqlalchemy import (
Text,
text,
)
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.dialects.postgresql import JSONB, UUID as PGUUID
from sqlalchemy.orm import relationship
from sqlalchemy.types import JSON
from app.models.base import Base, TimestampMixin, UUIDMixin
@@ -61,8 +60,8 @@ class Fact(Base, UUIDMixin, TimestampMixin):
# Confidence score (0.0 to 1.0)
confidence = Column(Float, nullable=False, default=0.8, index=True)
# Source tracking: which episodes contributed to this fact (stored as JSON array of UUID strings)
source_episode_ids: Column[list] = Column(JSON, default=list, nullable=False)
# Source tracking: which episodes contributed to this fact (stored as JSONB array of UUID strings)
source_episode_ids: Column[list] = Column(JSONB, default=list, nullable=False)
# Learning history
first_learned = Column(DateTime(timezone=True), nullable=False)
@@ -86,6 +85,15 @@ class Fact(Base, UUIDMixin, TimestampMixin):
unique=True,
postgresql_where=text("project_id IS NOT NULL"),
),
# Unique constraint on triple for global facts (project_id IS NULL)
Index(
"ix_facts_unique_triple_global",
"subject",
"predicate",
"object",
unique=True,
postgresql_where=text("project_id IS NULL"),
),
# Query patterns
Index("ix_facts_subject_predicate", "subject", "predicate"),
Index("ix_facts_project_subject", "project_id", "subject"),
@@ -96,6 +104,10 @@ class Fact(Base, UUIDMixin, TimestampMixin):
"confidence >= 0.0 AND confidence <= 1.0",
name="ck_facts_confidence_range",
),
CheckConstraint(
"reinforcement_count >= 1",
name="ck_facts_reinforcement_positive",
),
)
def __repr__(self) -> str: