diff --git a/api/database.py b/api/database.py index a74b857..69a919b 100644 --- a/api/database.py +++ b/api/database.py @@ -27,8 +27,8 @@ class Feature(Base): name = Column(String(255), nullable=False) description = Column(Text, nullable=False) steps = Column(JSON, nullable=False) # Stored as JSON array - passes = Column(Boolean, default=False, index=True) - in_progress = Column(Boolean, default=False, index=True) + passes = Column(Boolean, nullable=False, default=False, index=True) + in_progress = Column(Boolean, nullable=False, default=False, index=True) def to_dict(self) -> dict: """Convert feature to dictionary for JSON serialization.""" @@ -39,8 +39,9 @@ class Feature(Base): "name": self.name, "description": self.description, "steps": self.steps, - "passes": self.passes, - "in_progress": self.in_progress, + # Handle legacy NULL values gracefully - treat as False + "passes": self.passes if self.passes is not None else False, + "in_progress": self.in_progress if self.in_progress is not None else False, } @@ -73,6 +74,18 @@ def _migrate_add_in_progress_column(engine) -> None: conn.commit() +def _migrate_fix_null_boolean_fields(engine) -> None: + """Fix NULL values in passes and in_progress columns.""" + from sqlalchemy import text + + with engine.connect() as conn: + # Fix NULL passes values + conn.execute(text("UPDATE features SET passes = 0 WHERE passes IS NULL")) + # Fix NULL in_progress values + conn.execute(text("UPDATE features SET in_progress = 0 WHERE in_progress IS NULL")) + conn.commit() + + def create_database(project_dir: Path) -> tuple: """ Create database and return engine + session maker. @@ -87,8 +100,9 @@ def create_database(project_dir: Path) -> tuple: engine = create_engine(db_url, connect_args={"check_same_thread": False}) Base.metadata.create_all(bind=engine) - # Migrate existing databases to add in_progress column + # Migrate existing databases _migrate_add_in_progress_column(engine) + _migrate_fix_null_boolean_fields(engine) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) return engine, SessionLocal diff --git a/mcp_server/feature_mcp.py b/mcp_server/feature_mcp.py index 1534bc1..2af499f 100755 --- a/mcp_server/feature_mcp.py +++ b/mcp_server/feature_mcp.py @@ -409,6 +409,7 @@ def feature_create_bulk( description=feature_data["description"], steps=feature_data["steps"], passes=False, + in_progress=False, ) session.add(db_feature) created_count += 1 @@ -459,6 +460,7 @@ def feature_create( description=description, steps=steps, passes=False, + in_progress=False, ) session.add(db_feature) session.commit() diff --git a/server/routers/features.py b/server/routers/features.py index ce0f388..1d02cca 100644 --- a/server/routers/features.py +++ b/server/routers/features.py @@ -72,7 +72,10 @@ def get_db_session(project_dir: Path): def feature_to_response(f) -> FeatureResponse: - """Convert a Feature model to a FeatureResponse.""" + """Convert a Feature model to a FeatureResponse. + + Handles legacy NULL values in boolean fields by treating them as False. + """ return FeatureResponse( id=f.id, priority=f.priority, @@ -80,8 +83,9 @@ def feature_to_response(f) -> FeatureResponse: name=f.name, description=f.description, steps=f.steps if isinstance(f.steps, list) else [], - passes=f.passes, - in_progress=f.in_progress, + # Handle legacy NULL values gracefully - treat as False + passes=f.passes if f.passes is not None else False, + in_progress=f.in_progress if f.in_progress is not None else False, )