fix: address critical issues in PR #75 agent scheduling feature

This commit fixes several issues identified in the agent scheduling
feature from PR #75:

Frontend Fixes:
- Add day boundary handling in timeUtils.ts for timezone conversions
- Add utcToLocalWithDayShift/localToUTCWithDayShift functions
- Add shiftDaysForward/shiftDaysBackward helpers for bitfield adjustment
- Update ScheduleModal to correctly adjust days_of_week when crossing
  day boundaries during UTC conversion (fixes schedules running on
  wrong days for users in extreme timezones like UTC+9)

Backend Fixes:
- Add MAX_SCHEDULES_PER_PROJECT (50) limit to prevent resource exhaustion
- Wire up crash recovery callback in scheduler_service._start_agent()
- Convert schedules.py endpoints to use context manager for DB sessions
- Fix race condition in override creation with atomic delete-then-create
- Replace deprecated datetime.utcnow with datetime.now(timezone.utc)
- Add DB-level CHECK constraints for Schedule model fields

Files Modified:
- api/database.py: Add _utc_now helper, CheckConstraint imports, constraints
- progress.py: Replace deprecated datetime.utcnow
- server/routers/schedules.py: Add context manager, schedule limits
- server/services/assistant_database.py: Replace deprecated datetime.utcnow
- server/services/scheduler_service.py: Wire crash recovery, fix race condition
- ui/src/components/ScheduleModal.tsx: Use day shift functions
- ui/src/lib/timeUtils.ts: Add day boundary handling functions

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-22 08:35:57 +02:00
parent 44e333d034
commit 0736b5ec6b
7 changed files with 226 additions and 72 deletions

View File

@@ -6,11 +6,27 @@ SQLite database schema for feature storage using SQLAlchemy.
"""
import sys
from datetime import datetime
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String, Text, create_engine, text
def _utc_now() -> datetime:
"""Return current UTC time. Replacement for deprecated _utc_now()."""
return datetime.now(timezone.utc)
from sqlalchemy import (
Boolean,
CheckConstraint,
Column,
DateTime,
ForeignKey,
Integer,
String,
Text,
create_engine,
text,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, relationship, sessionmaker
from sqlalchemy.types import JSON
@@ -65,6 +81,14 @@ class Schedule(Base):
__tablename__ = "schedules"
# Database-level CHECK constraints for data integrity
__table_args__ = (
CheckConstraint('duration_minutes >= 1 AND duration_minutes <= 1440', name='ck_schedule_duration'),
CheckConstraint('days_of_week >= 0 AND days_of_week <= 127', name='ck_schedule_days'),
CheckConstraint('max_concurrency >= 1 AND max_concurrency <= 5', name='ck_schedule_concurrency'),
CheckConstraint('crash_count >= 0', name='ck_schedule_crash_count'),
)
id = Column(Integer, primary_key=True, index=True)
project_name = Column(String(50), nullable=False, index=True)
@@ -87,7 +111,7 @@ class Schedule(Base):
crash_count = Column(Integer, nullable=False, default=0) # Resets at window start
# Metadata
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
created_at = Column(DateTime, nullable=False, default=_utc_now)
# Relationships
overrides = relationship(
@@ -131,7 +155,7 @@ class ScheduleOverride(Base):
expires_at = Column(DateTime, nullable=False) # When this window ends (UTC)
# Metadata
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
created_at = Column(DateTime, nullable=False, default=_utc_now)
# Relationships
schedule = relationship("Schedule", back_populates="overrides")