
Introducing Acme.ai
Introducing Acme.ai, a cutting-edge AI solution for modern businesses.
Hybrid Scheduling System - Complete Architecture
Last Updated: 2025-12-02 Status: Design Phase - Intelligent Conflict Resolution with Graph-Based Optimization Version: 2.0 - All-or-Nothing Validation + Subject Reordering
Table of Contents
- System Overview
- Architecture Principles
- Algorithm Analysis
- Scheduling Workflow
- Conflict Resolution System
- All-or-Nothing Validation
- Subject Reordering Logic
- User Interface Design
- API Architecture
- Database Schema
System Overview
The hybrid scheduling system implements an intelligent constraint satisfaction approach with all-or-nothing guarantees and automatic conflict resolution through subject reordering.
Core Innovation: Conflict-Driven Reordering
Traditional Approach (❌ Suboptimal):
- Schedule Class A → complete
- Schedule Class B → teacher conflict → stop and fail
Our Approach (✅ Intelligent):
- Schedule Class A → complete
- Schedule Class B → teacher conflict detected
- Identify blocking class (Class A)
- Reorder Class A's subjects to free up teacher
- Reschedule Class A with new order
- Continue Class B - conflict resolved!
Key Features
- All-or-Nothing Validation: Class schedule either completes fully or fails cleanly with detailed diagnostics
- Graph-Based Optimization: Hybrid greedy + CSP backtracking for optimal performance
- Intelligent Reordering: Automatically reorders previously scheduled classes to resolve conflicts
- Actionable Error Messages: UI provides one-click resolution options (change teacher OR reorder subjects)
- Transaction Safety: Database rollback on any failure ensures consistency
Architecture Principles
Three-Phase Scheduling Architecture
graph TB
A[Phase 1: Configuration] --> B[Time Slots + Teachers]
B --> C[Phase 2: Validation]
C --> D{All Subjects<br/>Schedulable?}
D -->|No| E[Conflict Resolution]
D -->|Yes| F[Phase 3: Period Creation]
E --> G[Reorder Blocking Classes]
E --> H[Suggest Alternative Teachers]
E --> I[Show Detailed Diagnostics]
G --> J[Retry Validation]
J --> D
F --> K[Atomic DB Transaction]
K --> L[Complete Schedule]
I --> M[User Manual Resolution]
M --> A
Separation of Concerns
| Layer | Responsibility | Data Store |
|---|---|---|
| Slot Planning | MORNING/AFTERNOON preferences | timetables (teacherId: null) |
| Teacher Assignment | Per-subject teacher allocation | classSubjectProgress |
| Validation Engine | Check all constraints before creation | In-memory simulation |
| Period Generation | Create actual schedule atomically | periods (transaction) |
Algorithm Analysis
We compared four approaches to find the optimal scheduling algorithm:
Option A: CSP with Backtracking
function cspScheduling(classes: Class[]): Schedule {
for (const classToSchedule of classes) {
if (!canSchedule(classToSchedule)) {
// Backtrack and reorder previous classes
const conflictingClass = findBlockingClass(classToSchedule);
reorderAndReschedule(conflictingClass);
retry(classToSchedule);
}
}
}| Aspect | Assessment |
|---|---|
| Completeness | ✅ Guaranteed to find solution if one exists |
| Performance | ❌ Exponential O(n! × m!) - slow for 100+ classes |
| Determinism | ✅ Same input = same output |
| Best For | 10-50 classes per semester |
Option B: Graph-Based Greedy
function greedyScheduling(classes: Class[]): Schedule {
const conflictGraph = buildConflictGraph(classes);
const sortedClasses = topologicalSort(conflictGraph); // Most constrained first
for (const cls of sortedClasses) {
while (!canSchedule(cls)) {
const bestReordering = findMinimalConflictReordering(conflictGraph);
applyReordering(bestReordering);
}
}
}| Aspect | Assessment |
|---|---|
| Performance | ✅ Polynomial O(n² × m) - fast for 200+ classes |
| Completeness | ❌ May miss solutions (local minima) |
| Scalability | ✅ Handles large datasets well |
| Best For | 50-200 classes per semester |
Option C: Simulated Annealing
function annealingScheduling(classes: Class[]): Schedule {
let current = greedyInitialSchedule(classes);
let temperature = 1000;
while (temperature > 0.1) {
const neighbor = randomReordering(current);
const delta = evaluate(neighbor) - evaluate(current);
// Accept better OR probabilistically accept worse (to escape local minima)
if (delta > 0 || Math.random() < Math.exp(delta / temperature)) {
current = neighbor;
}
temperature *= 0.95; // Cool down
}
}| Aspect | Assessment |
|---|---|
| Quality | ✅ Near-optimal solutions |
| Determinism | ❌ Non-deterministic results |
| Complexity | ⚠️ Requires parameter tuning |
| Best For | 100+ classes, optimal quality needed |
Option D: Integer Linear Programming (ILP)
function ilpScheduling(classes: Class[]): Schedule {
const objective = minimizeTeacherConflicts;
const constraints = [
eachSubjectScheduledOnce,
teacherNotDoubleBooked,
respectDisplayOrder,
maxPeriodsPerDay,
];
return solver.solve(objective, constraints); // Google OR-Tools
}| Aspect | Assessment |
|---|---|
| Optimality | ✅ Mathematically provable optimal |
| Dependencies | ❌ Requires external solver library |
| Black Box | ❌ Harder to customize |
| Best For | Mission-critical scheduling |
⭐ Recommended: Hybrid Greedy + CSP
Best of both worlds: Fast for common cases, complete for edge cases.
function hybridScheduling(classes: Class[]): Schedule {
// Phase 1: Greedy (handles 90% of cases quickly)
const greedyResult = greedyScheduling(classes);
if (greedyResult.success) {
return greedyResult.schedule; // ✅ Fast path
}
// Phase 2: CSP Backtracking (handles complex conflicts)
console.log("⚙️ Greedy failed, using CSP backtracking...");
return cspBacktrackingSchedule(
greedyResult.partialSchedule,
greedyResult.failedClasses
);
}Performance Characteristics:
- Average Case: O(n² × m) - greedy dominates
- Worst Case: O(n! × m!) - CSP fallback (rare)
- Success Rate: ~95% greedy, ~5% CSP
Why Optimal:
- ✅ Fast for typical schedules (polynomial)
- ✅ Complete for edge cases (finds solution if exists)
- ✅ Production-Ready (reliable and debuggable)
- ✅ Graceful Degradation (smart fallback)
Scheduling Workflow
Complete End-to-End Flow
flowchart TD
A[User: Schedule Class Semester] --> B[Validate All Prerequisites]
B --> C{All Subjects<br/>Configured?}
C -->|No| D[Error: Configure subjects first]
C -->|Yes| E[START: Atomic Transaction]
E --> F[Phase 1: Simulation]
F --> G[For each subject in order]
G --> H[Simulate Period Creation]
H --> I{Teacher<br/>Available?}
I -->|Yes| J[Mark simulation success]
I -->|No| K[Conflict Detected!]
K --> L{Can Reorder<br/>Blocking Class?}
L -->|Yes| M[Reorder Algorithm]
L -->|No| N{Alternative<br/>Teacher Available?}
M --> O[Reschedule Blocking Class]
O --> P[Retry Current Subject]
P --> I
N -->|Yes| Q[Suggest Teacher Change]
N -->|No| R[FAIL: Impossible to Schedule]
J --> S{More<br/>Subjects?}
S -->|Yes| G
S -->|No| T[All Subjects Validated ✓]
T --> U[Phase 2: Period Creation]
U --> V[Create ALL Periods in DB]
V --> W[COMMIT Transaction]
W --> X[Success: Complete Schedule]
R --> Y[ROLLBACK Transaction]
Y --> Z[Show Conflict Diagnostics]
Z --> AA[User Action Required]
Q --> AA
D --> AA
AA --> AB{User<br/>Choice}
AB -->|Change Teacher| AC[Update Configuration]
AB -->|Reorder Subjects| AD[Automatic Reorder]
AB -->|Cancel| AE[Exit]
AC --> A
AD --> A
style E fill:#e3f2fd
style K fill:#fff3e0
style M fill:#fce4ec
style U fill:#e8f5e8
style W fill:#f3e5f5
style Y fill:#ffebeeConflict Resolution System
Conflict Detection & Classification
When scheduling a period for Class B, we check:
- Teacher Availability: Is teacher free at this date/time?
- If Occupied: Who is the teacher teaching? → Class A (blocking class)
- Subject Match: Is it the same subject?
- Reorder Feasibility: Can we reorder Class A's subjects?
Example Scenario
Setup:
- Class 10A (IT Major): Math, Physics, Programming
- Class 10B (IT Major): Math, Physics, Programming
- Teacher Nguyễn Văn A teaches Math for both classes
Initial Schedule (Class 10A already completed):
Monday 07:15 - Class 10A - Math - Teacher Nguyễn Văn A ✓
Monday 08:15 - Class 10A - Physics - Teacher Trần Thị B ✓
Tuesday 07:15 - Class 10A - Programming - Teacher Lê Văn C ✓
Scheduling Class 10B:
// Try to schedule Math for Class 10B at Monday 07:15
const conflict = checkTeacherAvailability(
teacherId: "Nguyễn Văn A",
date: "2025-01-06",
time: "07:15"
);
// Result:
conflict = {
isAvailable: false,
blockingClass: {
classId: 10A,
className: "Lớp 10A",
subject: "Math",
date: "2025-01-06",
time: "07:15"
}
}Resolution Options:
Option 1: Reorder Blocking Class (Class 10A)
// Current order: Math(1) → Physics(2) → Programming(3)
// New order: Physics(1) → Math(2) → Programming(3)
reorderClass({
classId: 10A,
newSubjectOrder: [
{ subjectId: 2, newOrder: 1 }, // Physics moves to 1
{ subjectId: 1, newOrder: 2 }, // Math moves to 2
{ subjectId: 3, newOrder: 3 } // Programming stays
]
});
// Result: Math moved to Tuesday 07:15
// Monday 07:15 now FREE for Class 10B!Option 2: Change Teacher for Class 10B
// Find alternative qualified teachers
const alternatives = getQualifiedTeachers({
subjectId: 1, // Math
available: true,
date: "2025-01-06",
time: "07:15",
});
// Result: [Teacher Phạm Thị D, Teacher Hoàng Văn E]
// Suggest to user: "Use Teacher Phạm Thị D instead?"All-or-Nothing Validation
Core Principle
Schedule entire class semester OR fail completely with diagnostics.
No partial schedules. No orphaned periods. Clean success or informative failure.
Implementation Architecture
sequenceDiagram
participant UI as User Interface
participant API as Scheduling API
participant V as Validation Engine
participant R as Reorder Engine
participant DB as Database
UI->>API: scheduleClassSemester(classId, semesterId)
API->>DB: BEGIN TRANSACTION
API->>V: Validate ALL subjects
loop For each subject
V->>V: Simulate period creation
V->>R: Check teacher availability
alt Teacher Available
V->>V: Mark subject valid ✓
else Conflict Detected
R->>R: Try reorder blocking class
alt Reorder Success
R->>V: Conflict resolved ✓
else Reorder Failed
R->>R: Find alternative teachers
alt Alternatives Found
R-->>UI: Suggest teacher change
UI-->>API: User decision
else No Solution
R-->>API: FAIL with diagnostics
API->>DB: ROLLBACK TRANSACTION
API-->>UI: Show conflict details
end
end
end
end
alt All Subjects Valid
API->>DB: CREATE all periods
API->>DB: COMMIT TRANSACTION
API-->>UI: Success ✓
else Validation Failed
API->>DB: ROLLBACK TRANSACTION
API-->>UI: Detailed error
endValidation Vs Creation Separation
Phase 1: Validation (Simulation)
const validationResults = await validateAllSubjects({
classId,
semesterId,
subjects,
});
// In-memory simulation - NO database writes
// Returns: {
// success: boolean,
// validSubjects: Subject[],
// conflicts: Conflict[],
// suggestions: Resolution[]
// }Phase 2: Period Creation (Atomic)
if (validationResults.success) {
await ctx.db.transaction(async (trx) => {
// Create ALL periods for ALL subjects
for (const subject of subjects) {
const periods = generatePeriodsForSubject(subject);
await trx.insert(periods).values(periods);
}
// Commits only if no errors
});
}Subject Reordering Logic
When to Reorder
Reordering is triggered when:
- Teacher conflict detected for current class
- Blocking class is identified (has same teacher + time slot)
ignoreDisplayOrderconfiguration allows reordering- Reordering creates a valid schedule
Reordering Algorithm: Greedy Conflict Minimization
function findOptimalReordering(
blockingClass: Class,
conflictingSubject: Subject,
targetTimeSlot: TimeSlot
): SubjectOrder[] {
const currentOrder = blockingClass.subjectOrder;
// Try all permutations of subjects (limited depth search)
const permutations = generateSmartPermutations(
currentOrder,
conflictingSubject
);
for (const newOrder of permutations) {
const simulation = simulateScheduleWithOrder(blockingClass, newOrder);
if (simulation.freesTargetTimeSlot && simulation.noNewConflicts) {
return newOrder; // ✅ Found valid reordering
}
}
return null; // ❌ No valid reordering found
}Smart Permutation Generation
Instead of trying all n! permutations, use heuristics:
- Local Swap: Try swapping conflicting subject with neighbors
- Move to End: Move conflicting subject to end of sequence
- Move to Start: Move conflicting subject to start
- Binary Search: Split subjects into before/after groups
function generateSmartPermutations(
currentOrder: Subject[],
conflictingSubject: Subject
): SubjectOrder[][] {
const index = currentOrder.indexOf(conflictingSubject);
const permutations = [];
// Strategy 1: Swap with next subject
if (index < currentOrder.length - 1) {
permutations.push(swap(currentOrder, index, index + 1));
}
// Strategy 2: Move to end
permutations.push(moveToEnd(currentOrder, index));
// Strategy 3: Move to position after lunch break
permutations.push(moveToAfternoon(currentOrder, index));
// Sort by predicted conflict reduction
return permutations.sort(
(a, b) => estimateConflicts(a) - estimateConflicts(b)
);
}Reordering Example
Scenario:
Class 10A (Blocking): Math → Physics → Programming
Class 10B (Current): Math → Physics → Programming
Conflict: Math @ Monday 07:15
Blocking Class: 10A
Target: Free up Monday 07:15
Reordering Process:
// Step 1: Identify subject to move (Math)
const subjectToMove = "Math";
// Step 2: Generate candidate reorderings
const candidates = [
["Physics", "Math", "Programming"], // Swap with next
["Physics", "Programming", "Math"], // Move to end
["Programming", "Physics", "Math"], // Full **reorder**
];
// Step 3: Simulate each candidate
for (const newOrder of candidates) {
const result = simulateReorder(Class10A, newOrder);
if (result.success && result.freesMonday0715) {
applyReordering(Class10A, newOrder);
return; // ✅ Conflict resolved
}
}After Reordering:
Class 10A (New Order): Physics → Math → Programming
Monday 07:15 - Class 10A - Physics ✓ (was Math)
Monday 08:15 - Class 10A - Math ✓ (moved from 07:15)
Tuesday 07:15 - Class 10A - Programming ✓
Monday 07:15 - Class 10B - Math ✓ (conflict resolved!)
Reordering Constraints
Reordering must respect:
- DisplayOrder Configuration: Only reorder if
ignoreDisplayOrderallows - No New Conflicts: Reordering must not create new teacher conflicts
- Minimal Disruption: Prefer smallest reordering (swap > move > full reorder)
- Transaction Safety: Reorder in same transaction as validation
User Interface Design
Conflict Resolution Modal
When scheduling fails, show actionable diagnostics:
interface ConflictDiagnostics {
message: string;
conflicts: Array<{
subject: string;
teacher: string;
timeSlot: string;
blockingClass: string;
blockingSubject: string;
}>;
resolutions: Array<{
type: "CHANGE_TEACHER" | "REORDER_SUBJECTS";
description: string;
actionLabel: string;
onClick: () => void;
}>;
}UI Mockup:
╔═══════════════════════════════════════════════════════════╗
║ ⚠️ Cannot Schedule Class 10B ║
╠═══════════════════════════════════════════════════════════╣
║ ║
║ Conflicts Detected (2): ║
║ ║
║ 1. Math @ Monday 07:15 ║
║ Teacher: Nguyễn Văn A ║
║ Conflict with: Class 10A - Math ║
║ ║
║ Suggested Solutions: ║
║ [🔄 Reorder Class 10A] [👤 Change Teacher] ║
║ ║
║ 2. Physics @ Tuesday 08:15 ║
║ Teacher: Trần Thị B ║
║ Conflict with: Class 9A - Chemistry ║
║ ║
║ Suggested Solutions: ║
║ [🔄 Reorder Class 9A] [👤 Use Teacher Lê Văn C] ║
║ ║
╠═══════════════════════════════════════════════════════════╣
║ [❌ Cancel] [🔍 View Details] ║
╚═══════════════════════════════════════════════════════════╝
Button Actions
Reorder Button:
const handleReorder = async (blockingClass: Class) => {
const confirmed = await showConfirmation({
title: "Reorder Class Schedule?",
message: `This will reschedule ${blockingClass.name} to resolve conflicts. Continue?`,
confirmLabel: "Reorder",
cancelLabel: "Cancel",
});
if (confirmed) {
await api.scheduling.reorderClassSubjects.mutate({
classId: blockingClass.id,
semesterId,
autoResolveConflicts: true,
});
// Retry current class scheduling
retryScheduling();
}
};Change Teacher Button:
const handleChangeTeacher = async (
subject: Subject,
alternativeTeacher: Teacher
) => {
await api.scheduling.updateSubjectConfig.mutate({
classId: currentClass.id,
semesterId,
subjectId: subject.id,
assignedTeacherId: alternativeTeacher.id,
});
// Retry scheduling with new teacher
retryScheduling();
};API Architecture
New Endpoints for Intelligent Scheduling
1. scheduleClassSemester (All-or-Nothing)
api.scheduling.scheduleClassSemester.useMutation({
input: {
classId: number;
semesterId: number;
autoResolveConflicts: boolean; // Enable automatic reordering
},
output: {
success: boolean;
periodsCreated?: number;
conflicts?: ConflictDiagnostics;
}
});Process:
- Validate all subjects can be scheduled
- If conflicts → attempt automatic reordering (if enabled)
- If still conflicts → return detailed diagnostics
- If all valid → create all periods atomically
- Return success or failure with actionable errors
2. validateClassSchedule (Simulation Only)
api.scheduling.validateClassSchedule.useQuery({
input: {
classId: number;
semesterId: number;
},
output: {
isValid: boolean;
conflicts: Conflict[];
suggestions: Resolution[];
}
});Purpose: Dry-run validation without creating periods.
3. reorderClassSubjects (Conflict Resolution)
api.scheduling.reorderClassSubjects.useMutation({
input: {
classId: number;
semesterId: number;
autoResolveConflicts: boolean;
},
output: {
success: boolean;
newOrder: SubjectOrder[];
periodsRescheduled: number;
}
});Process:
- Delete existing periods for class
- Find optimal subject ordering
- Recreate periods with new order
- Validate no new conflicts introduced
Database Schema
Key Tables
classSubjectProgress
ALTER TABLE class_subject_progress
ADD COLUMN canReorder BOOLEAN NOT NULL DEFAULT true,
ADD COLUMN reorderPriority INTEGER, -- Lower = less likely to be reordered
ADD COLUMN lastReordered TIMESTAMP;schedulingConflicts (New - Audit Trail)
CREATE TABLE scheduling_conflicts (
id SERIAL PRIMARY KEY,
classId INTEGER NOT NULL,
semesterId INTEGER NOT NULL,
subjectId INTEGER NOT NULL,
conflictType VARCHAR(50) NOT NULL, -- 'TEACHER_UNAVAILABLE', 'TIME_CONFLICT'
blockingClassId INTEGER,
blockingSubjectId INTEGER,
resolutionType VARCHAR(50), -- 'REORDER', 'CHANGE_TEACHER', 'MANUAL'
resolvedAt TIMESTAMP,
createdAt TIMESTAMP DEFAULT NOW()
);subjectReorderHistory (New - Track Reorderings)
CREATE TABLE subject_reorder_history (
id SERIAL PRIMARY KEY,
classId INTEGER NOT NULL,
semesterId INTEGER NOT NULL,
oldOrder JSONB NOT NULL, -- [{"subjectId": 1, "order": 1}, ...]
newOrder JSONB NOT NULL,
reason TEXT,
triggeredBy INTEGER, -- User ID or NULL for automatic
createdAt TIMESTAMP DEFAULT NOW()
);Performance Considerations
Optimization Strategies
- Early Termination: Stop validation on first unresolvable conflict
- Greedy First: Use fast greedy algorithm, fallback to CSP only if needed
- Memoization: Cache conflict checks for same teacher/time combinations
- Parallel Validation: Check multiple subjects concurrently (where independent)
- Smart Permutations: Limit reordering search space with heuristics
Complexity Analysis
| Operation | Average Case | Worst Case |
|---|---|---|
| Validation (No Conflicts) | O(n × m) | O(n × m) |
| Greedy Scheduling | O(n² × m) | O(n² × m) |
| CSP Backtracking | O(n × m × k) | O(n! × m!) |
| Reordering Search | O(m²) | O(m!) |
| Complete Schedule | O(n² × m) | O(n! × m!) |
Where:
- n = number of classes
- m = number of subjects per class
- k = average reordering attempts before success
Expected Performance:
- 50 classes × 10 subjects: ~2-5 seconds
- 100 classes × 10 subjects: ~10-20 seconds
- 200 classes × 10 subjects: ~30-60 seconds (greedy path)
Implementation Roadmap
Phase 1: All-or-Nothing Validation (Priority: HIGH)
- Implement simulation-based validation
- Add transaction wrapper for atomic creation
- Build conflict detection system
- Create detailed error diagnostics
Phase 2: Subject Reordering (Priority: HIGH)
- Implement greedy reordering algorithm
- Build conflict graph data structure
- Add reorder history tracking
- Create reorder API endpoints
Phase 3: UI Enhancement (Priority: MEDIUM)
- Design conflict resolution modal
- Implement actionable buttons (reorder/change teacher)
- Add real-time validation feedback
- Create progress indicators for long operations
Phase 4: Advanced Optimization (Priority: LOW)
- Implement CSP backtracking fallback
- Add simulated annealing option
- Build performance profiling dashboard
- Create batch scheduling API
Testing Strategy
Unit Tests
- Conflict detection logic
- Reordering algorithms
- Validation simulation
- Transaction rollback scenarios
Integration Tests
- End-to-end scheduling workflow
- Cross-class conflict resolution
- Database transaction safety
- API endpoint behavior
Performance Tests
- Large dataset scheduling (200+ classes)
- Worst-case CSP scenarios
- Concurrent scheduling requests
- Memory usage under load
User Acceptance Tests
- Conflict resolution flow
- Error message clarity
- Button action effectiveness
- Schedule visualization accuracy
Document Status: ✅ Complete Architecture Design - Ready for Implementation Approval